Você está na página 1de 37

Analisis y Diseo de un Analizador Sintctico

Se desea implementar un analizador lxico y sintctico de un pequeo subconjunto del lenguaje de consulta de Bases de Datos SQL. Como sab una sentencia de este lenguaje viene definida por tres clausulas o partes: la clausula SELECT, la clausula FROM y la clausula WHERE. Un ejemplo de una consulta que permite mostrar los nombres y apellidos de los estudiantes de edad superior a 20, es: select estudiantes.nombre, estudiantes.apellidos from estudiantes where (estudiantes.edad>20); Otro tipo de sentencias un tanto mas complejas son las que usan la funcin group by. Por ejemplo si queremos mostrar la edad media de los alumnos por titulacin a la que pertenecen y que estn en segundo curso. Hemos supuesto que toda esta informacin est en una nica tabla (Tabla estudiantes). select avg(estudiantes.edad) from estudiantes where (estudiantes.curso=2) group by estudiantes.titulacion; Otro caso a considerar es el caso en el que la informacin se encuentre en varias tablas. Por ejemplo, supongamos que queremos mostrar los nombres de las asignaturas del alumno con DNI 25400000. select asignaturas.nombre from estudiantes, asignaturas where (estudiantes.DNI=2540000) and (estudiantes.modulo=asignaturas.modulo) Anlisis Lxico (1 sesin)

Se trata de implementar un analizador lxico, en el que exista una funcin que devuelva el siguiente tokendentro del programa fuente, es decir, que devuelva el tipo de token en forma de constante entera, y su lexema, en forma de cadena. En esta primera fase de la prctica, y con la finalidad de comprobar que funciona correctamente, esta funcin ser llamada por la funcin main, que solicitar nuevos tokens hasta que se agote el texto del fichero de que contiene las consultas en SQL. El programa deber imprimir una lista de tokens de la forma (tipo_de_token, lexema) como resultado, detenindose en el caso de un error lxico e indicando la lnea y columna del texto fuente donde ste se produjo. Los componentes lxicos a reconocer son los siguientes: TKN_NUM = digito+ (. digito+)? TKN_ID = letra (letra | digito)* siendo digito = 0 | 1 | ... | 9 letra = a | b | ... | z | A | B | ... | Z Smbolos especiales TKN_APAR ( TKN_CPAR ) TKN_PTOCOMA ; TKN_PTO . Palabras reservadas: SELECT TKN_SELECT FROM TKN_FROM WHERE TKN_WHERE GROUP TKN_GROUP BY TKN_BY Funciones sobre campos agregados: AVG TKN_AVG //calcula el promedio SUM TKN_SUM //calcula la suma MAX TKN_MAX //calcula el mximo MIN TKN_MIN //calcula el mnimo Operadores: TKN_MENOR <

TKN_MAYOR > TKN_MENORIG <= TKN_MAYORIG >= TKN_IG = TKN_DISTINTO != TKN_Y and TKN_O or NOTA: La distincin entre identificadores y palabras reservadas debe hacerse mediante el mtodo de aadir las palabras reservadas a una lista de tokens predefinidos y comprobar cada candidato si es un identificador o palabra reservada. Anlisis Sintctico (2 y 3 sesin) Partiendo del analizador lxico ya programado, se trata de implementar un analizador sintctico direccional determinista descendente basado en el uso de una gramtica LL(1). El programa deber imprimir como resultado la lista de producciones que generan la derivacin ms a la izquierda. En el caso de que haya errores sintcticos, el programa debe proporcionar la informacin sobre la lnea y columna del texto original donde se produjo el error, e intentar efectuar una recuperacin del error en modo de pnico.

Diseo de una gramtica LL(1) Construir una Gramtica LL(1) que genere el lenguaje a reconocer: un subconjunto del lenguaje SQL. - Una sentencia SQL se compone de al menos de la clausula SELECT y FROM. La clausula WHERE y GROUP BY son opcionales. Si aparecen todas el orden es: SELECT, FROM, WHERE y GROUP BY. - En la clausula SELECT pueden aparecer varios identificadores separados por comas, o funciones de agregados.

- Los identificadores pueden venir indicados mediante el atributo o bien mediantenombre_de_la_tabla.atributo. - En la clausula FROM pueden aparecer varios nombres de tablas separados por comas. - En la clausula WHERE pueden aparecer combinaciones de expresiones relacionales y operadores lgicos. Recordad que los operadores lgicos tienen mayor prioridad que los relacionales y que puede existir anidamiento en las expresiones. - Supondremos que el programador puede escribir ms de una sentencia SQL consecutiva. - Una sentencia viene separada de otra por el carcter ; Implementacin del analizador sintctico descendente Implementar el analizador sintctico predictivo recursivo correspondiente para la gramtica que habis diseado. Implementacin de un sencillo traductor dirigido por la sintaxis Imaginemos que queremos ensear SQL a una persona que no sabe ingls y que queremos traducir todos los ejemplos de nuestro manual de referencia al castellano (o valenciano). Es decir, donde aparece SELECT, debe aparecer la palabra SELECCIONAR. Igual para el resto de palabras reservadas. Aade las acciones semnticas a tu cdigo para que durante el proceso de anlisis sintctico se realice la traduccin y vuelque a un fichero de salida las sentencias traducidas. Tambin habra que traducir los operadores AND por Y, OR por O. El primer ejemplo traducido a castellano sera: seleccionar estudiantes.nombre, estudiantes.apellidos desde estudiantes donde (estudiantes.edad>20); Implementacin de un mecanismo de recuperacin de errores Implementa un sencillo mecanismo de recuperaciones de errores sintcticos por el mtodo de recuperacin en modo de pnico.

Modifica la especificacin lxica del analizador lxico y crea una nueva especificacin y accin de un analizador sintctico para poder realizar un anlisis sintctico y generar un fichero html (generador pginas Web) a partir de una fuente escrito en L-02. Al programa generador Web se le pasa como argumento un fichero en especificacin L-02 y debe generar: - fichero html (PEDIDO EN LA PRCTICA ANTERIOR) - fichero texto con la lista de tipos de definiciones de elementos y n columna y fila donde aparecen (PEDIDO EN LA PRCTICA ANTERIOR) - fichero texto con los errores encontrados (preferible que muestre informacin sobre el error, del tipo columna y fila del fuente donde ha ocurrido, etc.). (NUEVO) El generador Web debe detectar los errores e informar de ellos a travs del fichero de errores. Para realizar el tratamiento de errores debemos tener en cuenta las siguientes condiciones que se imponen a una especificacin L02 vlida: - la seccin Pagina siempre debe existir - la seccin Cabecera puede o no existir, pero si existe debe ir acompaada de la declaracin Titulo - la seccin Cuerpo siempre debe existir. - El resto de errores que crea oportunos detectar el alumno. En este caso, se

debe indicar en la documentacin que se ha ampliado el tratamiento de errores y las nuevas situaciones de errores que detecta. Construyendo un analizador sintctico Un analizador sintctico, es una clase que recibe lo siguiente: 1. Una clase donde se almacenarn los atributos de cada smbolo. 2. Una gramtica libre de contexto donde el smbolo de inicio solo produce una variable. 3. Una funcin de sntesis para cada produccin, encargada de sintetizar los atributos necesarios. 4. Para cada produccin una opcional regla para deshacer ambigedades. 5. Una instancia de una clase de lxico que derive de la clase abstracta de lxico bsico. 6. Una opcional funcin de error para manejarlos y recuperarse en la medida de lo posible. El analizador ira leyendo del lxico y aplicando las reducciones necesarias (llamando a las funciones de sntesis proporcionadas). El resultado habitual es un rbol sintctico construido de manera ascendente. Cada nodo de ese rbol es del tipo genrico que se le pasa como parmetro al parser mencionado en la lista anterior.

El tipo de los nodos


El tipo de los nodos solo tiene un requisito de base. Es necesario que tenga un campo llamado ``text'' accesible con el operador ``.'' para que el sintctico almacene los tokens en las hojas del rbol. Este campo no contendr datos en los nodos superiores a menos que las funciones de sntesis lo usen para algo. Como normalmente se aplica el patrn visitor, el tipo de los nodos sera algo como esto:

struct lval_t { string text; nodeTree *node; }; La clase nodeTree sera de un tipo abstracto que derivar en los distintos tipos de construcciones sintcticas. Las funciones de sntesis sern las encargadas de construir estos nodos a partir de los hijos. Estas tienen el siguiente prototipo: V join_copia(vector<V>::iterator v); Donde V es el tipo de los nodos, como por ejemplo el ``lval_t'' definido antes. Como se ve, una funcin de sntesis toma un iterador a un vector de nodos. Este vector es realmente la pila de valores. La razn por la que se pasa un iterador es porque este apunta ya directamente a la parte de arriba de la pila que se tiene que reducir. Con la STL, este iterador se comporta como un vector, as que en realidad es como si estuviramos pasando un vector de nodos hijos del que se va a construir. Un ejemplo sera el siguiente: imaginemos que tenemos una produccin como la que sigue:

Su funcin de sntesis podra ser la siguiente: lval_t join_suma(vector<lval_t>::iterator v) { lval_t resultado; resultado.node=new nodeSuma(v[0],v[2]); //aqui v[1] seria el '+' en el campo text, no se usa return resultado; }

Conviene usar siempre punteros en la estructura ``lval_t'' o como se la llame para que no se tengan que hacer copias de los objetos.

Las funciones de sntesis


Primero definimos el tipo que va a almacenar los atributos de cada nodo del rbol. Como nosotros no vamos a construir un rbol sino que vamos a interpretar el cdigo en tiempo de ``sintctico'', lo nico que necesitamos guardar es el valor de la expresin. As pues ponemos un entero y el campo obligatorio text. struct lval { string text; int num; }; Veremos ahora el cdigo de algunas de las funciones de sntesis. Por ejemplo la de suma : lval join_suma(vector<lval>::iterator v) { lval res; res.num=v[0].num+v[2].num; return res; } Sencillo, simplemente se suman el primer y el tercer elemento. El segundo es el operador de suma que no nos sirve para nada. En esta filosofa el lxico est bien restringido, no hace cosas como convertir cadenas a nmeros. De esta manera no interfiere con la representacin interna del rbol. El sintctico y el lxico estn bien separados. Por eso es necesaria la variable num de la gramtica. Su

funcin de sntesis se encarga de interpretar la cadena de dgitos y convertirla a un nmero con la representacin que hemos elegido. lval join_int(vector<lval>::iterator v) { lval res; res.num=atoi(v[0].text.c_str()); return res; } Cuando se reduce un nmero desde un identificador, lo nico que se hace es sacar el valor de una tablahash. Veamos la funcin de sntesis de la produccin del operador de asignacin. lval join_asign(vector<lval>::iterator v) { lval res; variables[v[0].text]=v[2].num; res.num=v[2].num; cout<<"Asignado "<<v[2].num<<" a "<<v[0].text<<endl; return res; } No debera de haber problemas con todas las dems funciones que manejamos. Ms o menos es siempre lo mismo dado que es un lenguaje muy sencillo.

La funcin de error
La funcin de error, si se proporciona, es llamada por el parser siempre que encuentre un error sintctico a la entrada. El prototipo es fijo y se le dan una serie de datos que se consideraron necesarios.

bool merror(vector<lval>::iterator b,vector<lval>::iterator e, list<int> &ex,int &tok,lex_t &lex); Los dos primeros parmetros definen el rango en la pila de valores que fueron creados. En el caso de que el error sea irrecuperable la funcin ha de encargarse de liberar memoria reservada. El siguiente parmetro es la lista de tokens que el parser esperaba encontrarse en ese momento. Luego va el token que se encontr, pero pasado por referencia. Lo que quiere decir que la funcin de error podr modificar ese token para intentar recuperarse del error. Finalmente se pasa una referencia al lxico que se esta usando. Cuando la funcin haya terminado de hacer lo que tenga que hacer ha de devolver un bool. Si devuelve true el parser interpreta que el error es fatal y para el proceso de parsing. Si devuelve false el parser intentar seguir adelante, pero si la funcin no hizo nada con el token y el lxico que se le dio el error se volver a repetir. Nosotros vamos a construir una sencilla funcin de error que simplemente ignora los tokens errneos. bool merror(vector<lval>::iterator b,vector<lval>::iterator e, list<int> &ex,int &tok,lex_t &lex) { if(tok==T_EOF) { cout<<"Unexpected end of file, giving up...\n"; return true; } cout<<"Ignoring unexpected token "<<lex.text()<<" at line "<< lex.line()<<endl; tok=lex.nextToken();

return false;

Hacemos una excepcin con el token de final de fichero, que lgicamente no podemos ignorar. Cuando se encuentra un token que no se esperaba se ignora y se pasa al siguiente haciendo uso del lxico que se da.

Comportamiento del analizador sintctico El proceso de anlisis sintctico y la ejecucin son ahora dos pasos completamente separados, no se proceder a la ejecucin del cdigo de cualquier archivo hasta que ste en su totalidad, as como todo el cdigo requerido se haya analizado completa y satisfactoriamente_ Uno de los nuevos requisitos introducidos con esta separacin es que todos los archivos requeridos y de inclusin tienen que ser sintcticamente completos ahora_ Ya no es permitida la separacin de diferentes segmentos de una estructura de control a travs de varios archivos_ Esto quiere decir que ahora no puede iniciar un ciclo for o while, una sentencia if o un bloque switch en un archivo, y tener el final del ciclo, sentencias else, endif, case o break en un archivo diferente_

Aun es perfectamente legal incluir cdigo adicional al interior de ciclos u otras estructuras de control, nicamente las palabras claves de control y los corchetes correspondientes {___} tienen que estar en la misma unidad de compilacin (archivo o cadena procesada por eval()). Esto no debe generar una repercusin significativa ya que separar el cdigo de esta manera debe ser considerado como muy mal estilo, en cualquier caso. Algo ms que ya no es posible, aunque es rara veces visto en cdigo PHP 3, es devolver valores desde un archivo requerido.Devolver un valor desde un archivo de inclusin es posible aun.

Funcin del anlisis sintctico. Analizar sintcticamente una tira o cadena de tokens no es ms que encontrar para ella el rbol sintctico o de derivacin que tiene como raz el axioma de la gramtica, y como nodos terminales la sucesin ordenada de smbolos que componen la cadena analizada. En caso de no existir este rbol sintctico, la cadena no pertenecer al lenguaje, y el analizador sintctico ha de emitir el correspondiente mensaje de error.

Existen dos formas de analizar sintcticamente una cadena: Anlisis descendente: Partiendo del axioma inicial de la gramtica se va descendiendo utilizando las derivaciones izquierdas, hasta llegar a construir la cadena analizada.

Anlisis ascendente: Se va construyendo el rbol desde sus nodos terminales. Es decir, se construye desde los smbolos de la cadena hasta llegar al axioma de la gramtica. En este caso, se emplean normalmente las derivaciones ms a la derecha hasta la localizacin de la raz. Los principales mtodos de anlisis sintctico son:
o

Anlisis descendente:

Anlisis descendente con retroceso. Si bien en la prctica nunca se usa, se estudia a continuacin el anlisis descendente con retroceso, por ser el ms general de los anlisis descendentes, y por su valor didctico. Para la realizacin del anlisis descendente con retroceso contamos con una gramtica, cuyas reglas se enumeran: (1) A --> : B --> 3 : :
1(2)

A --> : : C -->

2 1

: : : : B --> : : :

: B -->

y de una cadena, supuestamente perteneciente al lenguaje definido por la gramtica: a1a2a3a4.... Paso 1. Inicialmente se parte de la forma sentencial S, que contiene nicamente el axioma de la gramtica. Paso 2. Dada una forma sentencial generada anteriormente, se sustituye el smbolo no terminal situado ms a la izquierda por la primera de las alternativas posibles para dicho smbolo, generando asi una nueva forma sentencial. Anotamos en una pila el nmero de la regla empleada.

En caso de que no existiera ningn smbolo no terminal en la forma sentencial, se habra llegado a producir la sentencia que estbamos analizando, estando almacenado su parse izquierdo en la pila; si no, se contina en el paso 4. Paso 3. Se anula la ltima produccin utilizada (situada en la cima de la pila), volviendo a obtener la forma sentencial anterior, y se retira de la pila el ltimo numero. En esta forma sentencial se utiliza ahora la siguiente de las alternativas disponibles para el smbolo no terminal situado ms a la izquierda, anotando en la pila el nmero de la regla empleada. En caso de que no haya ms alternativas, se repite el paso 3 para la forma sentencial obtenida. Si llega el momento en que se agotan todas las posibles alternativas del axioma inicial, (la pila se habra quedado vaca), entonces se puede garantizar que la cadena analizada no pertenece al lenguaje. Paso 4. Se comprueba si todos los smbolos terminales consecutivos que hayan aparecido a la izquierda de la forma sentencial encajan con alguna subcadena izquierda de la tira analizada. En caso afirmativo, se va alpaso 2. Si no, se va al paso 3. Ejemplo: Sea la gramtica: (1) S --> cAd
(2) A --> bcB (3) A --> a (4) B --> b

A continuacin, se va a realizar el anlisis (paso a paso) de la sentencia cad:

Paso 1: Inicialmente se parte de la forma sentencial S.


en las reglas de la gramtica la primera (y nica) alternativa para el smbolo S es S-->cAd. Empleando esta regla se obtiene la forma sentencial cAd. La pila contiene el nmero 1. Ir al paso 3. han aparecido a la izquierda de la formasentencial es c y la tira a analizar es cad. Por tanto, se supone vlida esta produccin y se contina por elpaso 2. no terminal es A, y la primera de las producciones para A es A->bcB. Aplicndola se obtiene la nueva forma sentencial cbcBd. La pila de anlisis contiene ahora el parse 1-2. ahora cbc, que no encaja con el principio de la tira analizada. Por tanto, vamos al paso 3. nuevamente la forma sentencial cAd. Retiramos el smbolo superior de la pila de anlisis, quedando sta como antes (es decir, 1). Se aplica ahora la segunda de las alternativas del smbolo no terminal A, que es A-->a, obtenindose la forma sentencial cad. En la pila de anlisis se introduce el nmero de la regla aplicada quedando sta como 1-3. que coincide exactamente con la tira analizada.

Paso 2: El smbolo no terminal situado ms a la izquierda es S,

Paso 3: La subcadena de smbolos terminales consecutivos que

Paso 2: Ahora la forma sentencial es cAd, cuyo primer smbolo

Paso 4: La subcadena izquierda de smbolos terminales es

Paso 3: Se anula la produccin anterior, obteniendo

Paso 4: La subcadena izquierda de smbolos terminales es cad, Paso 2: La forma sentencial no incluye ningn smbolo no

terminal, por lo que el proceso termina satisfactoriamente. El parse izquierdo de la cadena est contenido en la pila.

Es evidente que una condicin necesaria para poder realizar este tipo de anlisis, tal como se ha descrito anteriormente, es que la gramtica no sea recursiva por la izquierda y, en general, que no tenga ciclos por la izquierda. Por ejemplo, supongamos la gramtica: A --> Aa | a Segn el mtodo que se sigue para el anlisis, se generara la siguiente secuencia de formas sentenciales : A ==> Aa ==> Aaa ==> Aaaa ==> ... Igualmente sucede si la gramtica tiene ciclos, ya que por definicin de ciclo: A ==>+ A... Para solucionar este problema podemos hallar una gramtica equivalente no recursiva por la izquierda y sin ciclos, o bien modificar ligeramente el algoritmo. La implementacin en Pascal de este algoritmo, mediante el uso de un procedimiento recursivo, puede encontrase en el programa ejemplo RPM. Su ejecucin para la gramtica: 1. 2. 3. 4. 5. 6. 7. E --> T E --> T+E > T --> F T --> F*T > F --> a F --> b F --> (E)

y para el reconocimiento de la cadena

a*(a+b) da como resultado la siguiente secuencia. Pila 11-31-3-51-3-61-3-71-41-4-51-4-5-31-4-5-3-51-4-5-3-61-4-5-3-71-4-5-3-7-11-4-5-3-7-1-31-4-5-3-7-1-3-51-4-5-3-7-1-3-61-4-5-3-7-1-3-71-4-5-3-7-1-41-4-5-3-7-1-4-51-4-5-3-7-1-4-6Formas Sentenciales T F a b (E) F*T a*T a*F a*a a*b a*(E) a*(T) a*(F) a*(a) a*(b) a*((E)) a*(F*T) a*(a*T) a*(b*T)

1-4-5-3-7-21-4-5-3-7-2-31-4-5-3-7-2-3-51-4-5-3-7-2-3-5-11-4-5-3-7-2-3-5-1-31-4-5-3-7-2-3-5-1-3-51-4-5-3-7-2-3-5-1-3-6-

a*(T+E) a*(F+E) a*(a+E) a*(a+T) a*(a+F) a*(a+a) a*(a+b)

En la columna derecha se muestran las formas sentenciales que se van obteniendo durante el anlisis, y en la izquierda, los nmeros de las reglas utilizadas para producirlas (es decir, el parse izquierdo de cada una de estas formas senteciales). El ltimo valor obtenido en la columna izquierda es el parse izquierdo de la cadena analizada.

Procedimientos de anlisis LL(1).

Anlisis descendente sin retroceso. En el anlisis descendente con retroceso se generan formas sentenciales a partir del axioma, dando marcha atrs en cuanto se detecta que la forma generada no es viable, (es decir, no conduce a ninguna sentencia del lenguaje). Este proceso de vuelta atrs es lento. Para mejorar la eficiencia del mismo, sera muy til

saber a priori qu alternativa del smbolo no terminal es ms conveniente usar. Veamos de nuevo el ejemplo del apartado anterior: Ejemplo: Sea la gramtica (1) S --> cAd (2) A --> bcB (3) A --> a (4) B --> b y la sentencia cad. Partiendo del axioma, slo se puede aplicar la regla 1, obteniendo la forma sentencialcAd. Si se compara con la sentencia cad, se observa que ambas comienzan con el caracter c. Por tanto, lasubcadena Ad ha de generar el resto de la sentencia, o sea, ad. En este instante existen dos alternativas que se pueden emplear para modificar la forma sentencial, que corresponden a la aplicacin de las reglas 2 y 3. La aplicacin de la regla 2 provoca la aparicin del carcter b al principio de la subcadena restante, mientras que la regla 3 provoca la aparicin del carcter a. Por tanto, como la subcadena que falta por generar para producir la sentencia final es ad (empieza por a), puede deducirse que en este instante la regla que debe emplearse es la regla 3, y no la 2. El mtodo de anlisis que hemos seguido consiste en leer la cadena de entrada de izquierda a derecha, (L:Left to rigth) utilizando reglas de produccin izquierda (L: Left most) e inspeccionando un (1) solo smbolo de la entrada para elegir la regla conveniente. Este anlisis se denomina LL(1). Desafortunadamente, hay casos en los que este procedimiento no sirve. Supngase, por ejemplo, que lagramatica fuese:

(1) S --> cAd (2) A --> aB (3) A --> a (4) B --> b Al analizar la tira de entrada cad, tras realizar la primera produccin obtendramos la forma sentencialcAd, quedando como subcadena a analizar ad (que comienza con a). Pero ahora hay dos reglas aplicables que comienzan por a (las reglas nmero 2 y 3). Por tanto, no es posible decidir de forma automtica qu regla debe emplearse. Por otra parte, si se pretende que el anlisis sea sin retroceso, es indispensable que la gramtica no tenga ciclos por la izquierda (y consiguientemente, que no sea recursiva por la izquierda). No todas las gramticas admiten un anlisis descendente sin retroceso en el que se pueda predecir la alternativa que debe usarse. En el siguiente apartado se ver una condicin necesaria y suficiente para que una gramtica admita un anlisis LL(1). Anlisis ascendente: Anlisis ascendente con retroceso. El anlisis ascendente, como ya se ha visto, consiste en construir el rbol sintctico de una cadena dada, partiendo de los nodos terminales del mismo, hasta llegar a su raz. Estos nodos terminales no son otros que los distintos smbolos terminales de la cadena de entrada. En todos los anlisis ascendentes se plantea la necesidad de reducir una subcadena de nodos, para obtener as una metanocin, o

smbolo no terminal, comn a todos los smbolos de la subcadena. A esta operacin se le llama reduccin. Por otra parte, a las sucesivas lecturas de un smbolo de la cadena de entrada se les llamadesplazamientos. Cualquier anlisis ascendente utilizar estas dos operaciones, adems de las operaciones aceptar y error, que determinan el final del proceso. El primero de los mtodos ascendentes que vamos a ver es el anlisis ascendente con retroceso. Este anlisis, al igual que todos los mtodos con retroceso, se basa en la prueba sistemtica de todas las combinaciones posibles de reducciones y desplazamientos, continuando con el proceso siempre que sea posible, y dando marcha atrs cuando no quede otra alternativa. En la cadena de entrada, y en las distintas formas sentenciales que se van a ir generando para su reconocimiento, se distinguen dos subcadenas: la primera de ellas corresponde a los smbolos que se han leidohasta el momento; y la segunda ser la subcadena que queda por leer. Cada vez que se lee un smbolo, se realiza el desplazamiento de un smbolo de la subcadena derecha a la subcadena izquierda, o lo que es lo mismo, se desplaza una posicin la separacin entre las subcadenas izquierda y derecha. Inicialmente, se parte de la cadena de entrada, de la cul se ha ledo el primer smbolo. Por tanto, lasubcadena izquierda contiene un solo smbolo, y la subcadena derecha el resto. El algoritmo siguiente se repite hasta que se obtiene el axioma en la subcadena izquierda, estando la derecha vaca.

La forma sentencial que se est analizando en un instante dado se puede representar como , siendo lasubcadena izquierda y la subcadena derecha. Si tomamos = X1X2....Xp...Xn, y = Y1Y2....Ym, entonces = X1X2....Xp...XnY1Y2....Ym sera la forma sentencial analizada. Se va a considerar que las reglas gramaticales son de la forma: A1 -> Z1 1 Z1 2 .... Z1 S1A2 -> Z2 1 Z2 2 .... Z2 S2: -> Zk 1 Zk 2 .... Zk Sk : : :Ak

Como ya se ha anticipado, llamamos reduccin de la cadena , segn la regla k, a la sustitucion de los (n-p+1) smbolos derechos de la cadena que coinciden exactamente con los Sk smbolos del consecuente de la regla k, por el antecedente de dicha regla Ak. Por supuesto, slo ser posible efectuar la reduccin en el caso de que todos los smbolos del consecuente encajen con el extremo derecho de la subcadena . El resultado de la reduccin sera: = X1X2...Ak Se llama desplazamiento al paso del smbolo izquierdo de la cadena al extremo derecho de la cadena . Por supuesto, sto slo es posible si la longitud de es mayor que cero (es decir, si la cadena es distinta de la tira nula . El resultado sera: = X1X2....Xp...XnY1 = Y2....Ym Se describe a continuacin, en pseudo-codigo, el algoritmo recursivo de reconocimiento con retroceso que realiza el anlisis ascendente:

procedure Ensayar ( , );begin for k:=1 to Numero de reglas do if consecuente de regla k = Parte derecha de then begin Realizar Reduccion k, en la subcadena if =Axioma and = then Aceptar else Ensayar( , ) Anular Reduccion end if <> then begin Realizar un desplazamiento de un smbolo de a if =Axioma and = then Aceptar else Ensayar( , ) Anular Desplazamiento endend En el algoritmo anterior se ha supuesto que el procedimiento Aceptar conlleva la finalizacin del proceso de anlisis.

La implementacin prctica de dicho algoritmo corresponde al programa ejemplo RPMASC. El siguiente es un ejemplo de la ejecucin de este programa.

Ejemplo: Sea la gramtica gramtica: 1. 2. E -> E+T < E -> T

3. 4. 5. 6.

T -> T*F < T -> F F -> (E) F -> a

cuyo axioma es E. Los smbolos no terminales de la gramtica son {E,F,T}, y los smbolos terminales son{(,),*,+,a}. Tomando como entrada la cadena a+a*a, la traza de ejecucin del anlisis ascendente con retroceso para esta cadena es: Pila Expresin Regular a|+a*a 66-46-4-26-4-26-4-26-4-2-66-4-2-6-46-4-2-6-4-16-4-2-6-4-16-4-2-6-4-16-4-2-6-4-1-646-4-2-6-4-1-6F|+a*a T|+a*a E|+a*a E+|a*a E+a|*a E+F|*a E+T|*a E|*a E*|a E*a| E*F| E*T| Operacin Desplazamiento Reduccin Reduccin Reduccin Desplazamiento Desplazamiento Reduccin Reduccin Reduccin Desplazamiento Desplazamiento Reduccin Reduccin

4-2-

6-4-2-6-4-1-66-4-2-6-4-26-4-2-6-4-26-4-2-6-4-26-4-2-6-4-2-6-

E*E| E+E|*a E+E*|a E+E*a| E+E*F| E+E*T| E+E*E| E+T*|a E+T*a| E+T*F| E+T| E| E|

Reduccin Reduccin Desplazamiento Desplazamiento Reduccin Reduccin Reduccin Desplazamiento Desplazamiento Reduccin Reduccin Reduccin Fin de proceso. Correcto

44-2-

6-4-2-6-4-2-66-4-2-6-4-2-66-4-2-6-46-4-2-6-46-4-2-6-4-66-4-2-6-4-6-3-

11-

6-4-2-6-4-6-36-4-2-6-4-6-3-

Procedimientos de anlisis LR(0). Para la construccin de tablas de anlisis LR(0) es preciso realizar unas definiciones previas: Se denomina tem LR(0) de una gramtica a una terna (A, , ) Nx(N T)*x(N T)*, siendo A-> una regla gramatical de P. En

general, los tems se escriben como una regla de produccin separada en dos partes por un punto: [A -> 1 2] Para hacer mayor nfasis, y distinguir los items de las reglas gramaticales colocaremos unos corchetes al referirnos a los items. Cada regla de la gramtica dar origen a uno o ms items. De hecho, la nica regla que da origen a un solo tem es aquella cuyo consecuente es la tira nula (X -> ), que genera el tem [X -> ] LLamamos tem completo a aquellos de la forma [A -> ] Al conjunto de todos los items de una gramtica lo denotaremos como . Ejemplo: Sea una gramtica con las reglas S -> E$ E -> E+T E -> E-T >E -> T T -> (E) T -> a A partir de la misma podemos hallar los siguientes items: = {[S -> E$] , [E -> E+T] , [E -> E-T], [S -> E$] , [E -> E+T] , [E -> E--T], [S -> E$] , [E -> E+T] , [E -> E-T], [E -> T] , [E -> E+T] , [E -> E-T], [E -> T] , [T -> (E)] , [T -> a] , [E -> T] , [T -> (E)] , [T -> a] , [T -> (E)] , [T -> (E)]} Funcin de cierre. Sobre un conjunto I de tems se define la funcin cierre(I) como el conjunto de tems resultante de aplicar las siguientes reglas: cierre(I) : P( ) ---> P( ) Regla 1. Todos los tems pertenecientes a I pertenecen tambin a cierre(I).

Regla 2. Si [A -> B ] es un tem perteneciente a cierre(I), y existe una regla de produccin de la formaB -> , entonces el tem [B -> ] pertenece a cierre(I). Regla 3. Repetir la regla anterior hasta que no se aada ningn nuevo tem al conjunto cierre(I). Ejemplo: Sea el conjunto I compuesto por el tem I = {[S -> E$]} Inicialmente se calcula cierre(I) por la primera de las reglas, obtenindose: cierre(I) = {[S -> E$]} Segn la segunda de las reglas se aade a cierre(I) todos aquellos tems de la forma [E-> ] , ya que el smbolo no terminal E aparece justo a la derecha del punto del primer tem. Queda, por tanto: cierre(I) ={[S -> E$],[E -> E+T],[E -> E-T],[E -> T]} Se vuelve a aplicar la regla 2 al conjunto obtenido anteriormente. En este caso tendremos que incorporar al mismo todos aquellos tems de la forma [E -> ] (que ya estn) y los de la forma [T -> ], con lo que resulta: cierre(I) = {[S -> E$],[E -> E+T],[E -> E-T], [E -> T],[T -> (E)],[T -> a]} Nuevamente se intenta la regla 2, pero esta vez no se aade ningn nuevo elemento a cierre(I), ya que tenemos todos los tems de la forma [E -> ] y de la forma [T -> ], y los nuevos tems que han aparecido tienen un smbolo no terminal a la derecha del punto separador, por lo que no generan ningn nuevo tem por la operacin de cierre. Funcin de transicin. Se define la funcin de transicin , que se aplica a un conjunto de tems y a un smbolo (terminal o no terminal) de la gramtica, y da como resultado un nuevo conjunto de tems. : P( ) x {N T} ------> P{ } (I,X) ----> (I,X)

(I,X) es igual al cierre del conjunto de todos los tems de la forma [A -> X ], tales que [A -> X ]pertenece a I. Ejemplo: Sea el conjunto de items I = {[S -> E$],[E -> E+T],[E -> E-T],[E -> T], [T -> (E)],[T -> a]} Y sea el smbolo X = E El conjunto (I,E) es: cierre({[S -> E$],[E -> E+T],[E -> E-T]})) En este caso el cierre coincide con el conjunto, y por tanto: (I,E) = {[S -> E$],[E -> E+T],[E -> E-T]} Para el mismo conjunto de tems I, y para el smbolo ( se obtendra: (I,() = cierre({[T -> (E)]}) = {[T -> (E)],[E -> E+T],[E --> E-T], [E -> T],[T -> (E)],[T -> a]} Para el mismo conjunto de items I, y para el smbolo a, se obtendra: (I,a) = {[T -> a]} Construccin de la coleccin de conjuntos de items LR(0). Sea C un conjunto de items LR(0) compuesto por los conjuntos de items C = {I0,I1,I2,...} Supondremos que la gramtica de partida tiene una sola regla asociada al axioma S. En caso de que stono sea as, se aade a la gramtica la regla S'->S, y se toma como axioma el smbolo S'. Esta nueva gramtica se denomina gramtica aumentada. El algoritmo empleado es el siguiente: Paso 1. Se construye el conjunto inicial de items I0, compuesto por todos los tems pertenecientes al cierre del primer tem asociado al axioma. Es decir: I0 = cierre([S'->]) La coleccin C de conjuntos de items tiene ya un elemento.

Paso 2. Para cada conjunto de items I perteneciente a la coleccin C, y para cada smbolo X (terminal o no terminal) de la gramtica se halla el conjunto (I,X). Si este conjunto es no vaco, y no pertenece ya a la coleccin C, se aade a la misma. Paso 3. Se repite el paso 2 hasta que no se incorpore ningn conjunto nuevo a la coleccin C. Este proceso es finito. Dado que el nmero de reglas de una gramtica es finito, y que el nmero de smbolos en el consecuente de cada una de las reglas tambin lo es, el nmero de posibles tems tambin lo es. Siendo finito el nmero de items es tambin finito el nmero de conjuntos de items que podemos formar a partir de ellos, por lo que el proceso descrito anteriormente tiene fin en un nmero finito de pasos. Ejemplo: Para la gramtica anterior S -> E$ E -> E+T E -> E-T >E -> T T -> (E) T -> a La coleccin de items estar compuesta por los doce conjuntos siguientes: I0 = {[S -> E$],[E -> E+T],[E -> E-T],[E -> T], [T -> (E)],[T -> a]} I1 = {[S -> E$],[E -> E+T],[E -> E-T]} I2 = {[E -> T]} I3 = {[T -> a]} I4 = {[T -> (E)],[E -> E+T],[E -> E-T],[E -> T], [T -> (E)],[T -> a]} I5 = {[E -> E$]} I6 = {[E -> E+T],[T -> (E)],[T -> a]} I7 = {[E -> E-T],[T -> (E)],[T -> a]} I8 = {[E -> E+T]}

I9 = {[E -> E-T]} I10 = {[T -> (E)],[E -> E+T],[E -> E-T]} I11 = {[T -> (E)]} Se dice que un tem [A -> 1 2] es vlido para una subcadena viable 1 (es decir, una subcadena que puede ser la parte izquierda de alguna forma sentencial del lenguaje) si existe una secuencia de derivaciones de la forma: S ==>* A ==>* 1 2 En general, un tem puede ser vlido para muchas subcadenas. El hecho de que un tem sea vlido para una determinada subcadena proporciona informacin acerca de qu es lo que se debe hacer al encontrar unasubcadena de este tipo en la pila, ya que si el tem no es completo (es decir, 2<> ), esto indica que an no se ha encontrado un pivote que pueda ser reducido. Por el contrario, cuando existe un tem completo vlido asociado a una determinada subcadena, (es decir, 2= ) es posible efectuar en la misma una reduccin mediante la aplicacin de la regla gramatical correspondiente. Para construir la mquina de anlisis LR(0) se utiliza el siguiente procedimiento: Paso 1. Los estados de la mquina LR(0) corresponden a cada uno de los conjuntos de items I de la coleccin C. El estado inicial ser el asociado al conjunto de items que contenga al tem asociado al axioma, [S' -> S] Paso 2. La tabla de transiciones es la definida por la funcin de transicin entre conjuntos de items. Como se ve, la tabla de transiciones est definida sobre el producto cartesiano de la coleccin C, o conjunto de estados de la mquina, por el conjunto de smbolos terminales y no terminales (N T) de la gramtica. Paso 3. La tabla de acciones para la mquina LR(0) depende exclusivamente del estado en el que se encuentre la mquina, ya que

el anlisis debe efectuarse sin inspeccionar ningn smbolo de la entrada. La accin a realizar en cada estado es la siguiente: Si el conjunto de tems asociado al estado contiene un solo tem, y ste es completo (es decir, si el nico tem del conjunto es de la forma [A -> ]), la accin asociada a dicho estado es la reduccin mediante la reglaA -> (Accin = Reduccin), salvo en el caso en que esta regla est asociada al axioma, en el que el anlisis concluye con la aceptacin de la cadena de entrada (Accin = Aceptar). En caso de que el conjunto de items asociado a un estado no contenga ningn tem completo, la accin a realizar es desplazamiento. (Accin = Desplazamiento) . Si existe algn estado cuyo conjunto de items asociado contiene ms de un tem, siendo alguno de ellos completo, se dice que la gramtica no cumple las restricciones LR(0), o simplemente que la gramtica no esLR(0), y el anlisis por este mtodo no es posible. Ejemplo: Para la gramtica anterior (0) S -> E$ (1) E -> E+T (2) E -> E-T (3) E -> T (4) T -> (E) (5) T -> a El conjunto de estados de la mquina LR(0) es: {0,1,2,3,4,5,6,7,8,9,10,11} siendo el estado inicial el 0. Las tablas de anlisis son: TABLA DE ACCIONES E stado 0 A ccin D

1 2
3

D R R
5

3 4 5 6 7 8
1

D A D D R R
2

9 1 1
4

0 1

D R

TABLA DE TRANSICIONES a 0 1 2 3 4 3 4 1 2 3 6 7 + ( 4 5 ) $ E 1 T 2

0 5 6 7 8 9 1 0 1 1 6 7 1 1 3 3 4 4 8 9

El reconocimiento de la cadena a-a+a$ sera el siguiente: Pila ( ( ( ( ( 0 0a3 0T2 0E1 0E1-7 Entra da aa+a$ o -a+a$ -a+a$ -a+a$ a+a$ a T o o Accin Desplazamient Reduccin T -> Reduccin E -> Desplazamient Desplazamient

1) 2) 3) 4) 5)

6) 7) 8) 9) 10) 11) 12) 13)

( ( ( ( ( ( ( (

0E1-7a3 0E2-7T9 0E1 0E1+6 0E1+6a3 0E1+6T8 0E1 0E1$5

+a$ +a$ +a$ a$

a E-T o o

Reduccin T -> Reduccin E -> Desplazamient Desplazamient Reduccin T -> Reduccin E Desplazamient Aceptar

$ $ $

a >E+T o

*** Un mtodo alternativo de plantear la construccin de la coleccin de conjuntos de items que formarn los estados de la mquina LR(0) es la aplicacin de la teora de autmatas finitos de la siguiente forma: Paso 1: Se construye un autmata finito no determinista AFN de la siguiente forma: AFN=(QN,TeN, N,q0N,FN) QN= = Conjunto de todos los items. TeN=N T q0N=[S' -> S]>. Primer tem asociado al axioma de la gramtica. F={[A -> ] pertenecientes a }. Items completos.

. La funcin de transicin entre estados de la mquina no determinista. Se define as : Por cada tem de la forma [A -> b ], donde b pertenece a T (conjunto de smbolos terminales de la gramtica) se tiene que: [A -> b ] pertenece a N([A -> b ],b) donde B pertenece a N (conjunto de smbolos no terminales de la gramtica). Si [A -> B ] pertenece a N([A -> B ],B), entonces [B -> ] pertenece a N([A -> B ], ), para todos los posibles valores de . Paso 2: Una vez construido el AFN se aplica el algoritmo de construccin de subconjuntos para hallar elAutmata Finito Determinista mnimo (AFDM), obteniendo as el mismo resultado que con el procedimiento descrito anteriormente en este epgrafe.

Los anlisis con retroceso se basan en la prueba sistemtica de todas las alternativas posibles, dando marcha atrs tan pronto como se detecte que el camino seguido es errneo. Pueden usarse para cualquier gramtica de contexto libre, aunque tienen tres grandes inconvenientes: Primero, emplean mucho ms tiempo para el anlisis que los dems analizadores, dependiendo ste incluso de la ordenacin de las reglasgramticales; Segundo, no dan un buen diagnstico de los errores que encuentran; Tercero, complican la generacin de cdigo cuando sta se realiza al par que el anlisis sintctico. Los mtodos ms eficientes de anlisis (tanto ascendente como descendente) no funcionan para todas las gramticas de contexto libre, sino slo para las gramticas que cumplen unas determinadas condiciones.

Afortunadamente, en la mayora de los casos, pueden encontrase para los lenguajes de programacin gramticas de tipo LL o LR que los generen. Para representar el rbol sintctico que conduce hasta una cadena se asigna a cada regla de la gramtica un nmero. Se define el parse como la secuencia ordenada de nmeros (de reglas) aplicadas para construir dicho rbol. Hay dos tipos de parse, que son: El parse-izquierdo: Son los nmeros de las reglas de derivacion izquierda utilizadas para generar la cadena a partir del axioma, por tanto correspondiente a un anlisis descendente. El parse-derecho: Son los nmeros de las reglas de derivacin derecha utilizadas para generar la cadena a partir del axioma, en orden inverso. El tomar el orden inverso viene condicionado por ser el anlisis ascendente el que normalmente utiliza las reglas de derivacin derecha, con lo que el orden en el que aparecen al realizar el anlisis es invertido.

Ejemplos:

Dada la gramtica 1. 2. 3. 4. 5. 6. 7. E --> T E --> T+E T --> F T --> F*T F --> a F --> b F --> (E)

y la sentencia "a*(a+b)" El parse izquierdo es: 1-4-5-3-7-2-3-5-1-3-6 y el derecho: 5-5-3-6-3-1-2-7-3-4-1 Simultneamente a la fase de anlisis sintctico, adems de reconocer las secuencias de tokens, y analizar su estructura, pueden realizarse una serie de tareas adicionales, como: Recopilar informacin de los distintos tokens y almacenarla en la tabla de smbolos. Realizar algun tipo de anlisis semntico, tal como la comprobacin de tipos. Generar cdigo intermedio. Avisar de los errores que se detecten.

Você também pode gostar