Está en la página 1de 40

4o Ingenier a Inform atica

II26 Procesadores de lenguaje


Analizador sint actico Esquema del tema
1. Introducci on 2. Gram aticas incontextuales 3. Algunas construcciones de los lenguajes de programaci on 4. Extensiones de las gram aticas incontextuales 5. An alisis descendente 6. Implementaci on del analizador sint actico 7. Resumen del tema

1.

Introducci on

El analizador sint actico tiene como objetivo encontrar las estructuras presentes en su entrada. Estas estructuras se pueden representar mediante el arbol de an alisis sint actico, que explica c omo se puede derivar la cadena de entrada en la gram atica que especica el lenguaje. Aunque en la pr actica es habitual que el arbol de an alisis no llegue a construirse, se trata de una abstracci on que nos permite entender mejor todo el proceso. Para construir la especicaci on sint actica de los lenguajes de programaci on, se suelen emplear gram aticas incontextuales, generalmente restringidas para que el an alisis se pueda realizar de manera eciente. Para que sea posible construir el arbol de an alisis, es necesario que la entrada no presente errores sint acticos. En caso de que los haya, el analizador debe informar de su presencia adecuadamente y, si es posible, intentar continuar el an alisis.

2.

Gram aticas incontextuales

Ya hemos comentado que las gram aticas incontextuales se emplean para especicar la sintaxis de los lenguajes de programaci on. Sabemos que son un paso intermedio entre las gram aticas regulares y las sensibles al contexto. Es natural preguntarse por qu e nos detenemos en ellas. Hay diversas razones: Tienen suciente capacidad expresiva para representar la mayor parte de las construcciones presentes en el compilador. Adem as, es un formalismo que puede aumentarse con sencillez para incorporar las restricciones que no son puramente incontextuales. Son razonablemente sencillas de dise nar. Pese a que el poder expresivo de las gram aticas sensibles al contexto es mucho mayor, su dise no es excesivamente complejo para ser empleado en la pr actica. Permiten un an alisis eciente en el caso general y muy eciente si se introducen una serie de restricciones que no suponen una limitaci on excesiva de su poder expresivo. Ahora recordaremos los conceptos m as importantes de las gram aticas incontextuales.

II26 Procesadores de lenguaje

2.1.

Denici on

Como seguramente recordar as, una gram atica incontextual es una cu adrupla G = (N, , P, S ) donde N es un alfabeto de no terminales. es un alfabeto de terminales, disjunto con N . P N (N ) es un conjunto de producciones o reglas. S N es el s mbolo inicial. Como ves, las producciones de una gram atica incontextual tienen u nicamente un no terminal en la parte izquierda.

2.2.

Derivaciones

El funcionamiento de las gram aticas se basa en el concepto de derivaci on. Comenzando por una cadena formada u nicamente por el s mbolo inicial, se aplican repetidamente las reglas de P hasta llegar a una cadena formada u nicamente por terminales. Aplicar una regla consiste simplemente en encontrar, dentro de la cadena actual, la parte izquierda de la regla y sustituirla por la parte derecha correspondiente. Por ejemplo, sea la gram atica: G = ({ S , E }, {id, +, *}, P, S ), con P = {S E, E E+E, E E*E, E id}; para derivar id+id*id podemos hacer: S
Ejercicio 1

E E+E E+E*E id+ E * E id+id* E id+id*id

Encuentra otra derivaci on de id+id*id.


Ejercicio* 2

Cu antas derivaciones distintas tiene la cadena id+id*id? De una manera m as formal, tenemos las siguientes deniciones: Derivaci on directa: deriva directamente en G si = 1 A 2 , = 1 2 y A P . Lo denotamos con . Derivaci on: deriva (en G) si hay una secuencia de cadenas {1 , 2 , . . . , n } de (N ) tal que = 1 , = n y i i+1 para 1 i < n. Lo denotamos con . Forma sentencial: Cadena de (N ) tal que S . Sentencia: forma sentencial perteneciente a . Lenguaje denido (o generado) por la gram atica G: L(G) = {x | S x}.

Analizador sint actico

2.3.

Convenios de notaci on

En lo sucesivo y con objeto de no tener que indicar expl citamente a qu e alfabeto (N o ) pertenece cada s mbolo o cadena, asumiremos unos convenios que permitir an deducirlo inmediatamente: Representamos los terminales con un tipo de letra mecanogr aco (a, c, +, :=), en negrita (identicador, entero) o entrecomillado ((, )). Los no terminales se representan encerrados entre angulos ( A , Programa ). Un s mbolo que puede ser terminal o no terminal, indistintamente, se representar a con alguna de las u ltimas letras may usculas del alfabeto latino (X , Y,. . . ). La cadenas de s mbolos terminales (elementos de ) se representan con la u ltimas letras min usculas del alfabeto latino (u, v , w,. . . ). Las cadenas de s mbolos terminales o no terminales (elementos de (N ) ) se representan con letras min usculas del alfabeto griego (, , ,. . . ). Adem as, cuando escribamos una gram atica, s olo mostraremos sus reglas y asumiremos que el s mbolo inicial es el que est a en la parte izquierda de la primera regla. Tambi en asumiremos que la gram atica no tiene ni s mbolos in utiles ni reglas in utiles. Esto es, para cada s mbolo o regla, habr a al menos una derivaci on de una sentencia del lenguaje que contenga ese s mbolo o regla.

2.4.

Algunas deniciones adicionales

Si una producci on tiene el s mbolo A en su parte izquierda, la llamamos A -producci on. Si su parte derecha es u nicamente la cadena vac a, decimos que es una -producci on. + Si deriva en uno o m as pasos, escribiremos . Las producciones del tipo A B se llaman producciones simples. Si tenemos que para + + alg un no terminal A A , decimos que la gram atica tiene ciclos. Si A A diremos que la gram atica tiene recursividad por la izquierda. An alogamente, la gram atica tiene recursividad por + la derecha si A A .

2.5.

Arboles de an alisis y ambig uedad

Supongamos que tenemos la gram atica G = ({ S }, {id, *, +}, P, S ) con las siguientes producciones: S S S S+S, S*S,

id.

La sentencia id*id+id, tiene distintas derivaciones: S S S S ... S+S S+S S+S S+S S * S + S S *id+ S id*id+ S id*id+id, S +id S * S +id S *id+id id*id+id, S * S + S id* S + S id*id+ S id*id+id, S * S + S S *id+ S S *id+id id*id+id,

Sin embargo, en cierto sentido, todas estas derivaciones representan una misma estructura. De alguna manera nos transmiten la idea de que se est a sumando el producto de los dos primeros id
c Universitat Jaume I 2008-2009

II26 Procesadores de lenguaje

con el tercero. Podemos representar esto de forma compacta mediante un arbol como el siguiente: S

id

id

id

Esto es lo que se conoce como arbol de an alisis o de derivaci on. Intuitivamente, lo que hacemos es representar en paralelo las reglas necesarias para derivar la cadena de entrada. La ra z es el s mbolo inicial de la gram atica y cada nodo interior representa una producci on: est a etiquetado con un no terminal A y sus hijos de izquierda a derecha est an etiquetados con X1 , X2 , . . . , Xn de modo que A X1 X2 . . . Xn es una regla de la gram atica. Puede suceder que una misma cadena tenga asociado m as de un arbol de derivaci on. Por ejemplo, la cadena anterior tiene asociados dos arboles, cada uno con un signicado distinto: S S

id

id

id

id (id id) + id

id id (id + id)

id

Decimos que una sentencia es ambigua (con respecto a una gram atica) si tiene m as de un arbol de an alisis. Si al menos una de las sentencias que se derivan de una gram atica es ambigua, decimos que la gram atica es ambigua. En algunos casos, es posible resolver la ambig uedad modicando la gram atica adecuadamente, como veremos para el caso de las expresiones aritm eticas. Sin embargo, existen lenguajes que tienen la propiedad de ser inherentemente ambiguos; no existe ninguna gram atica no ambigua que los genere. Est a claro que la ambig uedad es perjudicial para un lenguaje de programaci on y debe ser evitada. Desgraciadamente, averiguar si una gram atica es ambigua es un problema indecidible. Lo que s que podremos garantizar es que las gram aticas pertenecientes a las familias que estudiaremos son no ambiguas.

2.6.

Derivaciones can onicas

Hemos visto que para una sentencia de la gram atica existen en general bastantes derivaciones posibles. De entre todas ellas, se suelen destacar dos, a las que se les llama derivaciones can onicas. Estas se caracterizan por aplicar las reglas de manera sistem atica sobre el no terminal m as a la izquierda (derivaci on can onica por la izquierda ) o m as a la derecha (derivaci on can onica por la derecha ). As una derivaci on can onica por la izquierda de id*id+id es: S S + S S * S + S id* S + S id*id+ S id*id+id.

Analizador sint actico

Cada arbol de derivaci on tiene asociada una derivaci on can onica por la izquierda y otra por la derecha. Por eso, si la sentencia es ambigua, habr a, al menos, dos derivaciones can onicas por la izquierda y otras tantas por la derecha. Dado que la gram atica del ejemplo es ambigua, tenemos otra posible derivaci on por la izquierda para la expresi on: S S * S id* S id* S + S id*id+ S id*id+id.

Las derivaciones can onicas por la derecha de id*id+id son: S S + S S +id S * S +id S *id+id id*id+id, S S * S S * S + S S * S +id S *id+id id*id+id.

Es sencillo obtener, a partir del arbol de an alisis, las correspondientes derivaciones can onicas por la derecha y por la izquierda: La derivaci on can onica por la izquierda se obtiene recorriendo el arbol de modo que se visita cada nodo, se escribe la regla correspondiente, y despu es, recursivamente, se visitan sus hijos de izquierda a derecha. La derivaci on can onica por la derecha se obtiene recorriendo el arbol de modo que se visita cada nodo, se escribe la regla correspondiente, y despu es, recursivamente, se visitan sus hijos de derecha a izquierda. En nuestro ejemplo, los ordenes de visita para obtener las derivaciones can onicas por la izquierda y por la derecha, respectivamente, son: S S

id

id

id
Ejercicio 3

id

id

id

Dada la gram atica con las reglas: E T F E+T|T T*F|F id

Encuentra el arbol de an alisis de id*id+id. A partir de este arbol, escribe la derivaci on can onica por la derecha y por la izquierda. Existe m as de un arbol de an alisis para esa sentencia?
Ejercicio* 4

Puede una sentencia tener innitos arboles de an alisis con una gram atica? Pista: piensa en una gram atica con ciclos.

c Universitat Jaume I 2008-2009

II26 Procesadores de lenguaje

3.

Algunas construcciones de los lenguajes de programaci on

Ahora veremos c omo se expresan algunas de las construcciones m as habituales de los lenguajes de programaci on mediante gram aticas. L ogicamente, la lista de construcciones presentadas no ser a completa. Debes tomarla m as como una lista de ejemplos que como una referencia. Es m as, las menciones que se hacen a C y Pascal no implican que esta sea la manera de escribir las estructuras en estos lenguajes; simplemente sirven para que tengas una idea de a qu e nos referimos.

3.1.

Estructura del programa

Los lenguajes tipo Pascal tienen una estructura similar a: Programa con: Cabecera Bloque Declaraciones program id; Declaraciones begin ListaSentencias end DConstantes DTipos DVariables DSubrutinas Cabecera Bloque .

Los lenguajes tipo C tienen una estructura similar a: Programa con: ListaDeclaraciones Declaraci on
Ejercicio 5

ListaDeclaraciones

Declaraci on ListaDeclaraciones | DeclVariable | DeclTipo | DeclFunci on

Construye una gram atica para un lenguaje tipo Pascal en que las declaraciones de constantes, tipos, variables y subprogramas puedan aparecer en cualquier orden y cualquier n umero de veces.

3.2.

Secuencias de instrucciones

Habitualmente se distinguen dos tipos de secuencias de instrucciones: Sentencias separadas por punto y coma (Pascal): ListaSentencias ListaSentencias Sentencia ; ListaSentencias Sentencia

Sentencias terminadas por punto y coma (C): ListaSentencias ListaSentencias Sentencia ListaSentencias Sentencia

En el segundo caso, las producciones para Sentencia deben incorporar el punto y coma, ya que, aunque informalmente se dice que C es un lenguaje en que las sentencias terminan en punto y coma, no todas lo hacen. Por ejemplo, la sentencia compuesta no termina en punto y coma, lo que hace que las sentencias de control de ujo tampoco terminen en punto y coma si afectan a una sentencia compuesta.

Analizador sint actico

Ejercicio 6

Qu e se hace en Pascal para que una lista de sentencias separadas por punto y coma pueda, en determinadas circunstancias, terminar en un punto y coma?

3.3.

Sentencia compuesta

La sentencia compuesta es simplemente una lista de sentencias encerrada entre dos marcadores adecuados. Por ejemplo, en Pascal: Sentencia begin ListaSentencias end

En C es posible incluir declaraciones dentro de las sentencias compuestas: Sentencia { Declaraciones ListaSentencias }

En C++ se pueden intercalar las declaraciones con las sentencias: Sentencia { ListaSentenciasYDeclaraciones }

3.4.

Declaraciones de variables
Un tipo precediendo a una lista de identicadores (C): DeclVariables ListaIds Tipo ListaIds ; Id , ListaIds | Id

Podemos distinguir dos tipos de declaraciones de variables:

donde Id es un identicador, posiblemente afectado por modicadores como * o corchetes. Un tipo siguiendo a una lista de identicadores (Pascal): DeclVariables ListaIds ListaIds : Tipo

id, ListaIds |id

3.5.

Declaraci on de subrutinas

Las subrutinas suelen declararse como una cabecera seguida de un bloque (Pascal): DeclSubrutinas Cabecera Bloque

Si no hay anidamiento, en lugar de un bloque se puede emplear una lista de declaraciones y sentencias delimitadas (C): DeclSubrutinas Cabecera { Declaraciones ListaSentencias }

3.6.

Estructuras de control condicional

La estructura de control condicional puede llevar un n expl cito como en: Sentencia Sentencia if Condici on then ListaSentencias endif if Condici on then ListaSentencias else ListaSentencias endif

Si no lo lleva, podemos utilizar las reglas siguientes: Sentencia Sentencia


c Universitat Jaume I 2008-2009

if Condici on then Sentencia if Condici on then Sentencia else Sentencia

II26 Procesadores de lenguaje

El problema de esta construcci on es que es ambigua. Sea la siguiente forma sentencial: if Condici on then if Condici on then Sentencia else Sentencia Un arbol de derivaci on es: Sentencia

if

Condici on

then

Sentencia

else

Sentencia

if

Condici on

then

Sentencia

En este caso, el else pertenece al primer if . Pero tambi en tenemos el arbol: Sentencia

if

Condici on

then

Sentencia

if

Condici on

then

Sentencia

else

Sentencia

Este caso se corresponde a la regla habitual de asociar el else al if m as cercano. Para incorporar esta regla a la gram atica, tendremos dos no terminales que reejar an si los if -then tienen que llevar obligatoriamente un else o no. El no terminal Sentencia podr a derivar condicionales con y sin parte else y el resto de sentencias. Por otro lado, las sentencias que se deriven del no terminal ConElse ser an condicionales completos o sentencias de otro tipo. La gram atica queda:

Sentencia

| |

if Condici on then Sentencia if Condici on then ConElse else Sentencia OtrasSentencias if Condici on then ConElse else ConElse OtrasSentencias

ConElse

En la pr actica, esta modicaci on complica la construcci on del analizador por la presencia de prejos comunes (lo veremos m as adelante en este mismo tema) y lo que se hace es manipular el analizador sint actico para que cumpla la regla.
Ejercicio 7

Encuentra el arbol de derivaci on de if id then if id then print else return con la gram atica: Sentencia | | ConElse OtrasSentencias Condici on | if Condici on then Sentencia if Condici on then ConElse else Sentencia OtrasSentencias if Condici on then ConElse else ConElse OtrasSentencias print | return id

Analizador sint actico

Ejercicio* 8

Nos han propuesto la siguiente gram atica para resolver el problema de la ambig uedad del if -then:

Sentencia Sentencia ConElse ConElse

if Condici on then Sentencia ConElse OtrasSentencias if Condici on then ConElse else Sentencia

Demuestra que esta gram atica es ambigua.

3.7.

Estructuras de control repetitivas

Las estructuras de control repetitivas suelen tener una condici on al principio: Sentencia o al nal: Sentencia repeat ListaSentencias until Condici on while Condici on do Sentencia

Si queremos que en un bucle del primer tipo se admitan varias sentencias, sin utilizar la sentencia compuesta, necesitamos alg un tipo de terminador: Sentencia while Condici on do ListaSentencias endwhile

Si dentro de la estructura de control se permite la salida prematura del bucle, por ejemplo con break, tenemos dos alternativas para especicar que s olo pueden aparecer dentro del bucle: Reejarlo directamente en la gram atica. El problema es que la soluci on inmediata es m as complicada de lo que parece a primera vista. Si probamos con: Sentencia SentenciasConBreak SentenciasConBreak SentenciasConBreak repeat SentenciasConBreak until Condici on Sentencia SentenciasConBreak break; SentenciasConBreak

Tenemos el problema de que break puede estar, por ejemplo, dentro de un condicional o una sentencia compuesta. Esto obliga a que esencialmente haya que replicar las reglas de Sentencia en un nuevo no terminal, por ejemplo SentenciaOBreak para incluir esta posibilidad. Tratar el break como una sentencia m as y a nadir una comprobaci on durante el an alisis sem antico. Esto es un ejemplo de lo que suele suceder al dise nar el compilador: determinadas fases se pueden simplicar seg un c omo se hayan dise nado las otras. Aqu simplicamos el an alisis sint actico a costa de complicar ligeramente el sem antico.
c Universitat Jaume I 2008-2009

10

II26 Procesadores de lenguaje

Ejercicio* 9

Reescribe la gram atica siguiente para excluir la posibilidad de que break aparezca fuera del bucle.

Programa Declaraciones Declaraciones Declaraciones Compuesta ListaSentencias Sentencia Sentencia Sentencia Sentencia Sentencia Sentencia Expresi on

Declaraciones Compuesta

variable Declaraciones constant Declaraciones begin ListaSentencias end Sentencia ListaSentencias | Compuesta

repeat ListaSentencias until Expresi on if Expresi on then Sentencia if Expresi on then Sentencia else Sentencia break other id | cte

3.8.

Expresiones aritm eticas

Si queremos permitir operaciones en notaci on inja con los operadores de suma, resta, producto, divisi on y exponenciaci on, podemos utilizar una gram atica como la siguiente: E | | | | | | E+E E-E E*E E/E EE ( E ) id

Como ya te habr as dado cuenta, por su similitud con el ejemplo de la secci on 2.5, esta gram atica es ambigua. Por ejemplo, la expresi on id+id+id, tiene dos arboles de an alisis: E E

id

id

id

id

id

id

Analizador sint actico

11

Lo habitual es elegir la primera interpretaci on, siguiendo las reglas de asociatividad. Normalmente, se sigue el siguiente convenio: Los operadores de suma, resta, producto y divisi on son asociativos por la izquierda. El operador de exponenciaci on es asociativo por la derecha.
Ejercicio 10

Qu e arbol de an alisis asociamos a ididid? Sin embargo, la asociatividad no nos permite resolver la ambig uedad que aparece cuando hay operadores distintos en la expresi on. Por ejemplo, id*id+id tiene dos arboles asociados: E E

id

id

id

id

id

id

Para estos casos utilizamos reglas de prioridad. Habitualmente: El operador de exponenciaci on es el m as prioritario. Los operadores de multiplicaci on y divisi on tienen la misma prioridad, que es menor que la de la exponenciaci on y mayor que la de la suma y resta. Los operadores de suma y resta tienen la misma prioridad, y esta es la m as baja. Con estas reglas, el arbol que utilizar amos ser a el de la izquierda.
Ejercicio 11

Qu e arbol de an alisis corresponde a la cadena id+id*idid*id+id? Para dise nar una gram atica que recoja tanto la asociatividad como las prioridades, utilizaremos las siguientes ideas: Cada nivel de prioridad se asociar a con un no terminal. La asociatividad por la izquierda se reejar a en recursividad por la izquierda; la asociatividad por la derecha en recursividad por la derecha. En nuestro caso, tendremos que: Una expresi on ser a bien la suma de una expresi on con un t ermino, bien la diferencia entre una expresi on y un t ermino, bien un u nico t ermino. Un t ermino ser a bien el producto de un t ermino por un factor, bien el cociente entre un t ermino y un factor, bien un u nico factor. Un factor ser a bien una base elevada a un factor, bien una u nica base. Una base ser a un identicador o una expresi on encerrada entre par entesis. De esta manera, nuestra gram atica es: E T F B
c Universitat Jaume I 2008-2009

E+T|E-T|T T*F|T/F|F BF|B id|( E )

12

II26 Procesadores de lenguaje

Ejercicio 12

Halla el arbol de an alisis de id*id(id+id). El a nadir operadores unarios no supone mayor complicaci on. Si queremos completar la gram atica anterior para que acepte el operador de cambio de signo con una prioridad mayor que la exponenciaci on, har amos: E T F B
Ejercicio* 13

E+T|E-T|T T*F|T/F|F BF|B id|( E )|- B

Analiza -id-id con la gram atica anterior y con esta: E T F B Por qu e son distintas? Es habitual que los operadores no aparezcan totalmente separados sino en categor as que asocien operadores con caracter sticas similares. Por ejemplo, en casi todos los contextos donde puede aparecer un + tambi en puede aparecer un -. Esto nos lleva a agruparlos en la categor a opad (por operador aditivo). Si tambi en creamos la categor a opmul para los operadores multiplicativos, nuestra gram atica queda: E T F B E opad T | T T opmul F | F BF|B id|( E )|opad B E+T|E-T|T T*F|T/F|F B F |- F | B id|( E )

F jate en que ahora se acepta como expresi on v alida +id. Para evitarlo, lo que se hace es que el analizador sem antico la rechace. Eso tiene adem as la ventaja de que los mensajes de error del sem antico suelen ser m as informativos. Este es otro ejemplo de simplicaci on del an alisis sint actico a costa de una ligera complicaci on del sem antico.
Ejercicio 14

Dene conjuntos de producciones adecuados para modelar las siguientes construcciones de un lenguaje de programaci on: Una declaraci on de variables consiste en un identicador de tipo (id, int o oat) seguido de una lista, separada por comas, de identicadores, cada uno de los cuales puede estar seguido del signo igual y una expresi on de inicializaci on. Una declaraci on de funci on consiste en un identicador de tipo o en la palabra reservada void, un identicador que da nombre a la funci on, una lista de par ametros formales entre par entesis y un cuerpo de funci on. Una lista de par ametros formales es una sucesi on (posiblemente vac a) de par ametros formales separados por comas. Un argumento formal empieza opcionalmente por la palabra var seguida de un identicador, dos puntos y un identicador de tipo. Un elemento de un vector se referencia mediante un identicador seguido de uno o m as ndices. Cada ndice es una expresi on aritm etica encerrada entre corchetes.

Analizador sint actico

13

4.

Extensiones de las gram aticas incontextuales

Ahora veremos c omo podemos extender la denici on de gram atica incontextual de modo que resulte m as f acil escribirlas. La extensi on que utilizaremos tiene el mismo poder expresivo que la denici on habitual, por lo que conservar a sus propiedades.

4.1.

Deniciones

Una gram atica con partes derechas regulares (GPDR) es una cu adrupla G = (N, , P, S ) donde N es un alfabeto de no terminales. es un alfabeto de terminales, disjunto con N . P N R(N ) es un conjunto de producciones o reglas. S N es el s mbolo inicial. Usamos R(N ) para representar el conjunto de las expresiones regulares b asicas sobre N . Recuerda que las expresiones regulares b asicas se constru an a partir del conjunto vac o, la cadena vac a, los s mbolos del alfabeto y las operaciones de disyunci on, concatenaci on y clausura. Para simplicar la formulaci on, en esta parte, excluiremos el conjunto vac o del conjunto de expresiones regulares. As pues, una gram atica con partes derechas regulares es similar a una gram atica incontextual con la diferencia de que la parte derecha de las reglas contiene expresiones regulares formadas a partir de terminales y no terminales. Dado que hemos modicado la denici on de gram atica, tendremos que cambiar la denici on de derivaci on. Ahora las formas sentenciales son expresiones regulares sobre N . Las reglas que se aplican son: 1. A , si A pertenece a P . Esta es la denici on habitual. 2. (1 |2 | . . . |n ) i , si 1 i n. Es decir, si tenemos una disyunci on, podemos escoger una cualquiera de las alternativas. 3. () . Si tenemos una clausura, podemos sustituirla por la cadena vac a. 4. () () . Si tenemos una clausura, podemos sustituirla por una copia de su base y una copia de ella misma. Como puedes imaginar, las reglas referentes a la clausura son las que dan su utilidad a las gram aticas con partes derechas regulares. El lenguaje denido por una gram atica con partes derechas regulares es: L(G) = {x | S x}. Esta es la misma denici on que para las gram aticas incontextuales, como era de esperar. Una GPDR para las declaraciones de variables consistentes en listas de identicadores seguidas por un tipo entero o real podr a ser: DeclVariables ListaVariables ListaVariables :( integer | oat );

id (, id)

c Universitat Jaume I 2008-2009

14

II26 Procesadores de lenguaje

Con esta gram atica, la derivaci on por la izquierda de id, id: integer; es: DeclVariables
1 4 1

ListaVariables :( integer | oat );

id (, id) :( integer | oat ); id , id (, id) :( integer | oat ); id , id:( integer | oat ); id , id: integer; (El n umero encima de las echas indica cu al de las reglas para la derivaci on se ha aplicado.)
2 3

4.2.

Equivalencia con las gram aticas incontextuales

Hemos armado que las GPDR tienen el mismo poder expresivo que las incontextuales. Esto quiere decir que si un lenguaje tiene una gram atica incontextual que lo represente, tambi en tendr a una GPDR. An alogamente, si un lenguaje tiene una GPDR, tambi en tendr a una gram atica incontextual. Ahora justicaremos estas armaciones. En primer lugar, parece claro que toda gram atica incontextual es un caso particular de GPDR. As la primera parte est a trivialmente demostrada. En cuanto a la segunda parte, utilizaremos un procedimiento constructivo. Empezamos con las reglas de nuestra gram atica. Iremos sustituyendo aquellas reglas que utilicen extensiones por otras que tengan menos extensiones. Finalmente, todas las reglas tendr an en la parte derecha u nicamente secuencias de terminales y no terminales, con lo que habremos obtenido la gram atica incontextual equivalente. Si la regla tiene la forma A (1 |2 | . . . |n ) , creamos un nuevo no terminal N y sustituimos la regla por el conjunto de reglas: A N N ... N n N 1 2

Si la regla tiene la forma A () , creamos un nuevo no terminal N y sustituimos la regla por el conjunto de reglas: A N N N N

Es f acil demostrar que estas transformaciones no cambian el lenguaje generado por la gram atica. Tambi en est a claro que cada vez que aplicamos una de las transformaciones se reduce en uno el n umero de extensiones de la gram atica. De esta manera, terminaremos con una gram atica incontextual sin partes derechas regulares. As , la gram atica que hemos empleado para las declaraciones de variables es equivalente a: DeclVariables DeclVariables2 ListaVariables ListaVariables2 ListaVariables2 ListaVariables : DeclVariables2 ; integer | oat id ListaVariables2 , id ListaVariables2

Analizador sint actico

15

Ejercicio 15

Transforma la siguiente GPDR en una gram atica incontextual: S Expr ListaId (print Expr |int ListaId );

id(+ id) id(, id)

Encuentra una derivaci on de int id, id; print id; con la gram atica original y con la nueva.

4.3.

Arboles de an alisis

A la hora de dibujar el arbol de derivaci on de una sentencia del lenguaje, existen dos losof as, que reejan dos formas de interpretar las GPDR. Una opci on es dibujar el arbol como si se hubiera transformado la GPDR en una gram atica incontextual equivalente. La otra opci on es considerar que las expresiones regulares en las partes derechas suponen de hecho condensar un n umero potencialmente innito de reglas. Seg un la primera interpretaci on, el arbol de an alisis de id, id: integer; con la GPDR para las declaraciones ser a: DeclVariables

ListaVariables

DeclVariables2

id

ListaVariables2

integer

, id

ListaVariables2

Si no queremos introducir los nuevos no terminales y mantenemos las expresiones regulares, podemos escribir:

DeclVariables

ListaVariables (, id)

( integer | oat )

id

integer

, id (, id)
c Universitat Jaume I 2008-2009

16

II26 Procesadores de lenguaje

Sin embargo, si utilizamos la segunda interpretaci on, obtenemos: DeclVariables

ListaVariables

: integer ;

id , id Esta u ltima interpretaci on es la que seguiremos: la primera manera de construir los arboles introduce una estructura que realmente no estaba presente en la gram atica.
Ejercicio 16

Construye el arbol de an alisis de int id, id; print id; con la gram atica del ejercicio 15.

4.4.

Reescritura de algunas construcciones

Ahora repasaremos algunas de las construcciones que hemos visto antes, adapt andolas a las GPDR. 4.4.1. Secuencias de instrucciones

Las secuencias de instrucciones separadas por punto y coma, las podemos expresar mediante la regla: ListaSentencias Sentencia (; Sentencia )

Si queremos sentencias terminadas en punto y coma, con el punto y coma expl cito: ListaSentencias
Ejercicio 17

( Sentencia ;)

La regla anterior permite que la secuencia de sentencias sea vac a, c omo puedes hacer que haya al menos una sentencia? 4.4.2. Declaraciones de variables

Si tenemos un tipo precediendo a una lista de identicadores (C): DeclVariables Tipo Id (, Id ) ;

El caso del tipo siguiendo a la lista de identicadores (Pascal): DeclVariables 4.4.3. Expresiones aritm eticas id(,id) : Tipo

Podemos simplicar las producciones correspondientes a las expresiones aritm eticas. Sin embargo, esta simplicaci on tiene un precio; no podremos expresar directamente en la gram atica la asociatividad de los operadores. Tendremos que relegar en fases posteriores la responsabilidad de denir la asociatividad. En la pr actica esto no supondr a ning un problema. Sin embargo, seguiremos teniendo un no terminal por cada nivel de prioridad. La gram atica que ten amos para las expresiones la podemos reescribir as : E T F B T ((+|-) T ) F ((*|/) F ) B ( B ) id|( E )

Analizador sint actico

17

Si agrupamos + junto con - en la categor a l exica opad (operador aditivo) y * junto con / en opmult (operador multiplicativo), nos queda una gram atica ligeramente m as clara: E T F B
Ejercicio 18

T (opad T ) F (opmult F ) B ( B )

id|( E )

Halla el arbol de an alisis de id*id(id+id).


Ejercicio 19

Aumenta la gram atica para permitir operadores unarios. Dales la m axima prioridad.
Ejercicio 20

Reescribe las construcciones del ejercicio 14 utilizando GPDR.

5.

An alisis descendente

Una vez denido nuestro lenguaje mediante una gram atica G, estamos interesados en resolver el siguiente problema: Dada una cadena x y una gram atica G, pertenece x a L(G)? Esto es lo que se conoce como el problema del an alisis. Existen diversos algoritmos que resuelven este problema de manera general. Dos de los m as conocidos son: Algoritmo de Cocke-Younger-Kasami o CYK. Este algoritmo utiliza el esquema algor tmico de Programaci on Din amica. Su coste es O(|x|3 ) y necesita una gram atica en forma normal de Chomsky. Algoritmo de Early este algoritmo tiene un coste igual al CYK si la gram atica es ambigua. Si no lo es, el coste se reduce a O(|x|2 ) aunque, para muchas gram aticas, el coste es O(|x|). Nuestro objetivo ser a buscar algoritmos que garanticen un coste lineal con la entrada. Para ello, tendremos que poner algunas restricciones a las gram aticas que se podr an utilizar. Dependiendo del modo en que se construye el arbol de an alisis, distinguimos dos familias de algoritmos de an alisis: An alisis descendente se construye el arbol partiendo de la ra z hasta llegar a las hojas. En este proceso se va prediciendo lo que vendr a despu es, por lo que tambi en se le denomina an alisis predictivo. An alisis ascendente se construye el arbol desde las hojas. La detecci on de las construcciones se realiza una vez que se han visto completamente. En este tema estudiaremos un tipo de an alisis descendente: el an alisis LL(1), que es f acil de implementar manualmente y es razonablemente potente; a nal de curso, estudiaremos m etodos de an alisis ascendente. Comenzaremos por ver c omo analizar las gram aticas incontextuales y despu es pasaremos a las GPDR.
c Universitat Jaume I 2008-2009

18

II26 Procesadores de lenguaje

5.1.

Un ejemplo

Supongamos que tenemos la siguiente gram atica: S S S E if E then S while E do S id:= E id|cte

Queremos analizar la sentencia if cte then id:=cte. Inicialmente sabemos que tendremos un rbol que en la ra a z tendr a el no terminal S que derivar a el resto. Adem as, sabemos que lo que derive S estar a seguido por el n de la entrada, que representaremos mediante $. En este momento, estaremos mirando el primer s mbolo de la entrada, el componente if . Podemos resumir esta informaci on de la siguiente manera: if S cte then id:=cte$ $ S

En la parte derecha tenemos el arbol que estamos construyendo (de momento s olo conocemos la ra z). El cuadro de la izquierda tiene en la parte superior la entrada que tenemos que analizar y en la inferior la predicci on del analizador. Hemos separado el primer s mbolo de cada parte para que quede claro qu e se est a comparando en cada momento. Mirando la gram atica, vemos que la u nica regla que podemos aplicar es la primera. La nueva situaci on es: S if if cte then id:=cte$ E then S $ if E then S

Este cuadro lo podemos interpretar de la siguiente manera: el analizador predice que va a ver un if seguido de algo que expanda E , un then, despu es algo que expanda S y nalmente el nal de la entrada. Dado que estamos viendo el if que se ha predicho, podemos avanzar en la entrada: cte E then id:=cte$ then S $

Nuevamente, analizando la gram atica, vemos que s olo podemos aplicar una regla: E cte. Cambiamos nuestra predicci on, y aumentamos ligeramente el arbol: S cte cte then id:=cte$ then S $

if

then

cte Como coinciden los terminales, avanzamos. Vuelven a coincidir y avanzamos nuevamente. Estos dos pasos son: then then id:=cte$ S$ id S :=cte$ $

Analizador sint actico

19

Ahora tenemos que encontrar una expansi on de S que comience por id, lo que supone aplicar la regla S id:= E . Aumentamos el arbol y llegamos a: S id id :=cte$ := E $

if

then

cte

id :=

Vuelve a haber coincidencias de terminales, hasta que nalmente llegamos a tener que expandir E por cte. Estos pasos son: S

if := := cte$ E$ cte E $ $ cte cte $ $ $ $

then

cte

id

:=

cte Hemos llegado a una situaci on en la que vemos que hemos predicho el n de la entrada y lo hemos encontrado. Esto quiere decir que el an alisis ha concluido con exito y ya tenemos el arbol de an alisis.

5.2.

El algoritmo de an alisis

Vamos a intentar transformar el ejemplo anterior en un algoritmo. En primer lugar, observemos que la predicci on que hacemos (la la de abajo en los cuadros) se comporta como una pila. Adem as podemos darnos cuenta de que hacemos dos tipos de acci on seg un sea el s mbolo en la entrada y la predicci on: Si hemos predicho un no terminal, vemos qu e regla de ese no terminal comienza por el s mbolo en la entrada y utilizamos la parte derecha de esa regla para completar nuestra predicci on. Si ambos son terminales, comprobamos si son iguales. Si lo son, avanzamos. Observando el primer paso, nos damos cuenta de que la informaci on acerca de qu e regla aplicar en cada caso, la podemos guardar en una tabla. Esta tabla tendr a, para nuestro ejemplo, el aspecto siguiente:

cte S E cte

do

id id := E id

if if E then S

then

while while E do S

:=

Las entradas vac as corresponden a errores sint acticos.


c Universitat Jaume I 2008-2009

20

II26 Procesadores de lenguaje

Si llamamos M a nuestra tabla, el algoritmo de an alisis es el siguiente: Algoritmo An alisis predictivo push(, $); push(, S ); // Predicci on inicial: S $ a := siguiente(); mientras top() = $ hacer X := pop(); si X N : // X es un no terminal si M [X, a] = ERROR entonces push(, M [X, a]1 ) si no devolver falso n si si no // X es un terminal si a = X entonces a := siguiente() si no devolver falso n si n si n mientras devolver a = $; n An alisis predictivo Este algoritmo devolver a cierto si la cadena pertenece al lenguaje denido por la gram atica y falso en otro caso. M as adelante veremos que nos interesar a ser a tratar los errores adecuadamente, pero de momento, esto nos vale. Utilizamos la pila para guardar la predicci on. F jate que cuando introducimos la nueva parte derecha en la pila, lo hacemos al rev es, para que sea analizada en el orden correcto. Si adem as estamos interesados en obtener el arbol, bastar a con tomar nota cada vez que se haga una predicci on de la regla utilizada. Como puedes comprobar, el coste del algoritmo es lineal con la longitud de la entrada. Lo que vamos a hacer ahora es buscar la manera de construir las tablas de manera autom atica a partir de la gram atica.
Ejercicio 21

Utiliza el algoritmo para analizar las cadenas: while cte do id:=id while cte if id:=id

5.3.

C alculo de primeros

En el ejemplo anterior hemos podido decidir en cada caso qu e producci on aplicar porque los primeros s mbolos de cada alternativa eran terminales diferentes. Sin embargo, exigir esta condici on para que podamos analizar cadenas con una gram atica es excesivo. Vamos a ver c omo podemos relajarla sin perder la capacidad de escribir la tabla de an alisis. Supongamos que modicamos la gram atica anterior para permitir que se emplee el operador unario * de modo que sea posible utilizar punteros. La nueva gram atica tiene este aspecto: S E I if E then S |while E do S | I := E I |cte * I |id

Ahora tenemos un problema para rellenar en la tabla las las de S y E : tenemos que saber cu ando utilizar las reglas que comienzan por I . Sin embargo, un an alisis r apido nos permite ver

Analizador sint actico

21

que las cadenas producidas por I s olo pueden comenzar por * o por id. As pues, la tabla de an alisis es la siguiente:

cte S E I cte

do

id I := E I id

if if E then S

then

while while E do S

* I := E I *I

:=

Vamos a formalizar el proceso de construcci on de la tabla. En primer lugar, para decidir entre las alternativas de una producci on, debemos saber c omo empiezan las cadenas que puede generar. Si tenemos una forma sentencial , podemos denir primeros() como:

primeros() = {a | a }. Es decir, primeros() es el conjunto de terminales que pueden ser el primer s mbolo de las cadenas generadas por 1 . Para escribir el algoritmo de c alculo de los primeros de una forma sentencial, necesitamos el concepto de anulable. Decimos que una forma sentencial es anulable (lo representamos como anulable()) si de ella se puede derivar la cadena vac a. Si la gram atica no tiene recursividad por la izquierda, podemos calcular el conjunto de primeros de mediante el siguiente algoritmo recursivo: Algoritmo primeros ( : (N ) ) C= si = a entonces C := {a}; si no si = A entonces para todo A P hacer C := C primeros( ); n para si anulable( A ) entonces C := C primeros( ); n si n si devolver C ; n primeros

El funcionamiento del algoritmo es sencillo: Si comienza por un terminal, este es el u nico primero. Si comienza por el no terminal A , tendremos que obtener los primeros de A . Adem as, si A es anulable tambi en habr a que mirar los primeros de lo que le sigue.

1 En algunos textos, cuando la cadena vac a puede ser derivada de , se la considera parte de los primeros de . Esto supone una leve modicaci on de la formalizaci on.

c Universitat Jaume I 2008-2009

22

II26 Procesadores de lenguaje

Podemos averiguar si es anulable mediante el siguiente algoritmo: Algoritmo anulable ( : (N ) ) si = entonces devolver cierto; si no si = a entonces devolver falso; si no si = A entonces si existe tal que A P y anulable( ) entonces devolver anulable( ); si no devolver falso; n si n si n anulable
Ejercicio 22

Calcula los primeros de los no terminales de la siguiente gram atica: S A B A B|B c

ab|cd b a |

Estos algoritmos sirven si la gram atica no tiene recursividad a izquierdas. En caso contrario, los c alculos de primeros y anulables se complican ligeramente (mira el ejercicio 24). Sin embargo, dado que las gram aticas con recursividad a izquierdas no son LL(1), no estudiaremos ese caso aqu y veremos m as adelante c omo eliminar la recursividad a izquierdas. Ahora podemos construir la tabla de an alisis de la siguiente manera: En primer lugar calculamos los primeros de las partes derechas de las reglas de la gram atica. Despu es rellenamos la celda M [ A , a] de la tabla con , si A P y a primeros(). Este m etodo de construcci on de la tabla funcionar a siempre y cuando ning un no terminal sea anulable.

5.4.

C alculo de siguientes

Qu e sucede cuando alg un no terminal es anulable? Vamos a estudiarlo aumentando la gram atica de nuestro ejemplo para permitir accesos a vectores: S E I A if E then S |while E do S | I := E I |cte * I |id A |[ E ]

Intentemos analizar la cadena id:=id[cte]. La situaci on inicial es: id S :=id[cte]$ $

Ahora podemos aplicar la regla S I := E y despu es I id A : id I :=id[cte]$ := E $ id id :=id[cte]$ A := E $

Analizador sint actico

23

Como coinciden los terminales, avanzamos: := A id[cte]$ := E $

Y ahora tenemos un problema. Evidentemente, := no est a entre los primeros de A . Por tanto, nos preguntamos si en esta situaci on podemos utilizar la regla A . Para encontrar la respuesta, debemos plantearnos cu ando esta regla es v alida. La podremos emplear cuando nos encontremos en la entrada algo que pueda ir detr as de A . Si examinamos la predicci on, vemos que, en este caso, la asignaci on efectivamente puede seguir a A , por lo que concluimos que es correcto sustituir A por la cadena vac a y continuar el an alisis. As , los pasos que siguen son := := id[cte]$ E$ id E [cte]$ $ id I [cte]$ $ id id [cte]$ A$ [ A cte]$ $

Ahora nos encontramos analizando uno de los primeros de A ; seguimos el an alisis como hasta ahora: [ [ cte]$ E ]$ cte E ]$ ]$ cte cte ]$ ]$ ] ] $ $ $ $

Y concluimos el an alisis con exito. Hemos visto que podemos aplicar una -producci on siempre que nos encontremos en la entrada alguno de los s mbolos que puedan aparecer tras el no terminal correspondiente. Vamos a ver c omo calcular estos s mbolos. Llamaremos siguientes de un no terminal A a los miembros del conjunto: siguientes( A ) = {a | S A , a primeros( )} {$} {a | S A , a primeros( )}

si S A , si no.

La idea es que los siguientes de un no terminal A son aquellos terminales que pueden llegar a aparecer detr as de A en alguna forma sentencial. En el caso del s mbolo inicial y de aquellos que pueden aparecer al nal de una forma sentencial, adem as incluimos el n de la entrada. Para encontrar los siguientes de cada no terminal, podemos emplear las siguientes ecuaciones: En primer lugar: {$} siguientes( S ). (1) Si A B P , entonces: primeros( ) siguientes( B ); si, adem as, anulable( ) entonces: siguientes( A ) siguientes( B ). (3) (2)

La ecuaci on (1) es f acil de entender: cualquier cadena generada a partir del s mbolo inicial estar a seguida del n de la entrada. Para entender (2), nos planteamos c omo se genera la cadena a1 . . . al b1 . . . bm c1 . . . cn a partir de A : A

a1 . . . al
c Universitat Jaume I 2008-2009

b1 . . . bm c1 . . . cn

24

II26 Procesadores de lenguaje

Es f acil ver qu e va detr as (los siguientes) de B : los primeros de . Por otro lado, si puede anularse, tenemos una situaci on como la siguiente: S

d1 . . . dj

e1 . . . ek

a1 . . . al b1 . . . bm

Con lo que B y A comparten siguientes, lo que explica la ecuaci on (3). Para resolver las ecuaciones podemos ir aplic andolas en tres pasos: 1. Como inicializaci on, asociamos a todos los no terminales distintos del inicial el conjunto vac o y al s mbolo inicial le asociamos el conjunto {$} para que se cumpla la ecuaci on (1). 2. Para cumplir con la ecuaci on (2), a nadimos al conjunto de siguientes de cada no terminal A los primeros de los tales que A constituye la parte derecha de alguna regla. 3. Recorremos ordenadamente las reglas aplicando la ecuaci on (3). Para ello, por cada regla A B con anulable, a nadimos los siguientes de A a los de B . Este paso tendremos que repetirlo hasta que no cambie ninguno de los conjuntos de siguientes en un recorrido completo de las reglas. Vamos a aplicar el algoritmo a nuestro ejemplo. En primer lugar, inicializamos los siguientes al conjunto vac o salvo para el s mbolo inicial: No terminal S E I A Siguientes {$}

Despu es, a nadimos los s mbolos que aparecen inmediatamente despu es de cada no terminal: No terminal S E I A Siguientes {$} {], do, then} {:=}

Ahora aplicamos ordenadamente la ecuaci on (3) hasta que no haya modicaciones. As , a partir de S I := E , aumentamos los siguientes de E : No terminal S E I A Siguientes {$} {], $, do, then} {:=}

Analizador sint actico

25

Con la regla E I , aumentamos los siguientes de I : No terminal S E I A Siguientes {$} {], $, do, then} {:=, ], $, do, then}

A partir de I id A , aumentamos los siguientes de A : No terminal S E I A Siguientes {$} {], $, do, then} {:=, ], $, do, then} {:=, ], $, do, then}

Si volvemos a repasar las reglas, vemos que los siguientes no var an, as que podemos dar por terminado el proceso. En algunos casos, hay que repetir el tercer paso m as veces. Por ejemplo, vamos a calcular los siguientes para la gram atica: A B C B c|C d

b|a A a B

Inicialmente, tenemos vac os los conjuntos de siguientes, salvo el de A : No terminal A B C Siguientes {$}

Ahora, por la ecuaci on (2), a nadimos c a los siguientes de B y d a los de C : No terminal A B C Siguientes {$} {c} {d}

La ecuaci on (3) s olo se puede aplicar a B a A y C a B . As que, copiamos los siguientes de B en los de A : No terminal A B C Siguientes {c, $} {c} {d}

c Universitat Jaume I 2008-2009

26

II26 Procesadores de lenguaje

Y los de C en los de B : No terminal A B C Siguientes {c, $} {c, d} {d}

Y todav a no hemos terminado. Como ha habido modicaciones en el u ltimo recorrido, tenemos que dar una nueva pasada. Copiamos los siguientes de B en los de A : No terminal A B C Siguientes {c, d, $} {c, d} {d}

Al copiar los siguientes de C en los de B no hay cambios. Haciendo ahora un u ltimo recorrido, vemos que no hay m as cambios y terminamos.
Ejercicio 23

Calcula los primeros y siguientes de los no terminales de la siguiente gram atica: S S S S LS Cond E Id A Id := E ; Cond Cond else LS S LS |

while E do LS endwhile

if E then LS cte| Id * Id |id A [ E ]|

Ejercicio* 24

Si la gram atica tiene ciclos, el c alculo de primeros y de anulables se hace mediante ecuaciones similares a las que hemos utilizado para el c alculo de siguientes. Escribe esas ecuaciones y pru ebalas con la siguiente gram atica: S E T M F E ; S | E+T|T T M F|F

*| id|( E )

Analizador sint actico

27

5.5.

An alisis LL(1)

Ahora estamos en condiciones de rellenar la tabla de an alisis. Los pasos que seguiremos son: Averiguar qu e no terminales son anulables. Calcular los primeros de las partes derechas de las reglas. Calcular los siguientes de los no terminales anulables. Por cada regla A , poner en las celdas M [ A , a] tales que: a primeros() o anulable() y a siguientes( A ). Si en cada celda hay como m aximo una parte derecha, decimos que la gram atica es LL(1) y podemos aplicar nuestro algoritmo de an alisis. El nombre LL(1) viene de: Left to right: el orden en que se lee la entrada. Leftmost derivation: se encuentra una derivaci on por la izquierda. 1 lookahead symbol: se utiliza un s mbolo de anticipaci on. La tabla de nuestro ejemplo es: cte do S E I A cte id if then while while E do S * I := E I * I [ E ] := [ ] $

I := E if E then S I id A

As pues, la gram atica era LL(1). Algunas propiedades de las gram aticas LL(1): No son ambiguas. No tienen recursividad por la izquierda. Los conjuntos de primeros de las partes derechas de un no terminal son disjuntos. Si A , los siguientes de A son disjuntos con los primeros de la parte derecha de cualquier A -producci on. Adem as, A tiene como mucho una parte derecha anulable. Hasta ahora hemos hablado de gram aticas LL(1). Si un lenguaje tiene una gram atica LL(1), decimos que ese lenguaje es LL(1). Existen lenguajes incontextuales que no son LL(1), es decir que no existe ninguna gram atica LL(1) capaz de modelarlos. Un ejemplo ser a: {an 0bn |n 1} {an 1b2n |n 1}. Afortunadamente, gran parte de las construcciones habituales de los lenguajes de programaci on pueden modelarse mediante gram aticas LL(1). 5.5.1. Conictos LL(1)

Si al rellenar la tabla de an alisis aparece una celda con m as de una entrada, tenemos lo que se llama un conicto LL(1). Esta situaci on indica que el analizador no sabe qu e debe predecir en ese momento. Normalmente, basta modicar ligeramente la gram atica para evitar estos conictos. Veremos ahora algunas de las posibles modicaciones.
c Universitat Jaume I 2008-2009

28

II26 Procesadores de lenguaje

Eliminaci on de la recursividad por la izquierda Si la gram atica tiene recursividad por la izquierda es seguro que aparecer a un conicto (por qu e?). Hay dos tipos de recursividad por la izquierda: Directa: existe al menos una regla de la forma A A . Indirecta: existe una derivaci on de la forma A A . En primer lugar, veremos c omo podemos eliminar la recursividad directa. Supongamos que tenemos el siguiente par de producciones: A A A
+

Las formas sentenciales que pueden generar son del tipo n con n 0. Podemos sustituirlas por las siguientes reglas, que generan las mismas formas sentenciales: A A A A A

En algunos casos, la eliminaci on no es tan sencilla y se requiere expandir primero alguna de las reglas para despu es aplicar la transformaci on anterior. Existe un algoritmo para eliminar completamente la recursividad por la izquierda. Por desgracia, la gram atica resultante no tiene ninguna relaci on con la original, lo que lo hace poco pr actico. Afortunadamente, en la mayor parte de las ocasiones, la recursividad por la izquierda se puede eliminar utilizando gram aticas con partes derechas regulares.
Ejercicio 25

Elimina la recursividad por la izquierda en la siguiente gram atica: E T F E+T|E-T|T T*F|T/F|F

( E )|id

Calcula su tabla de an alisis y analiza la cadena id*(id+id).


Ejercicio* 26

C omo podemos eliminar la recursividad directa por la izquierda en las siguientes reglas? A A A 1 | A 2 | . . . | A n

1 |2 | . . . |m

Eliminaci on de prejos comunes Otra de las causas de aparici on de conictos LL(1) es la existencia de prejos comunes entre distintas partes derechas, lo que lleva a que los distintos conjuntos de primeros no sean disjuntos. Para eliminar estos conictos, podemos aplicar el an alogo de sacar factor com un. Si tenemos las reglas: A |

Podemos sustituirlas por: A A A |

Analizador sint actico

29

Puede suceder que los prejos aparezcan indirectamente, entonces hay que expandir previamente alg un no terminal. Por ejemplo: S P id := E | P id ( E )

En este caso, deberemos expandir en primer lugar el no terminal P : S P Despu es aplicamos la factorizaci on: S S P id S := E |( E ) id ( E ) id:= E |id ( E ) id ( E )

Finalmente, si P no aparece en ninguna parte derecha, podemos eliminarlo.


Ejercicio 27

Elimina los prejos comunes de estas producciones: S S S S LS E try : S except id: S try : S except id: S nally : S id:= E ;| E ; { LS } S | S LS | id|cte

Manipulaci on directa de la tabla de an alisis En algunos casos, la mejor manera de resolver un conicto es cambiar directamente la tabla de an alisis. Por ejemplo, sea la gram atica: S S S E if E then S else S if E then S sent id

En primer lugar, eliminamos prejos comunes: S S E El Ahora escribimos la tabla de an alisis: else S E El else S id id if if E then S El sent sent then $ if E then S El sent id else S |

c Universitat Jaume I 2008-2009

30

II26 Procesadores de lenguaje

Vemos que hay un conicto en la celda correspondiente a El con el terminal else. Si analizamos en qu e situaciones podemos llegar al conicto, vemos que la u nica manera en la que podemos tener un El en el tope de la pila es por haber terminado de analizar la S de un if -then. Pero si seguimos la regla de asociar el else al if m as cercano, lo que tenemos que hacer en esta situaci on es predecir la secuencia else S . As pues, modicamos la entrada de la tabla y obtenemos: else S E El id id else S if if E then S El sent sent then $

5.6.

Gram aticas RLL(1)

Si la gram atica de la que partimos es una GPDR, tenemos dos opciones para construir un analizador. La m as obvia es transformarla, utilizando los m etodos que presentamos en el punto 4.2, en una gram atica incontextual para la que despu es construir amos el analizador. Sin embargo, vamos a ver c omo podemos construir los analizadores directamente a partir de las propias GPDR. Esto nos llevar a a los analizadores RLL(1), en cierto sentido los equivalentes a los LL(1) para GPDR. Estudiaremos la construcci on de manera intuitiva, sin entrar en una formalizaci on rigurosa. En primer lugar, debemos darnos cuenta de que ahora podr an aparecer en la pila de predicciones, y por lo tanto en la tabla, no s olo terminales y no terminales, sino tambi en expresiones regulares en forma de clausuras o disyunciones. Comenzaremos por un ejemplo para entenderlo mejor. Sea la GPDR: E T T ((+|-) T )

id|cte|( E )

Vamos a analizar la cadena id+cte. Utilizando nuestra representaci on habitual, empezamos por: id E +cte$ $ id T +cte$ ((+|-) T ) $ id id +cte$ ((+|-) T ) $

Como coinciden los terminales, podemos avanzar en el an alisis. Pero ahora nos encontramos con una importante diferencia, lo que avanza es la clausura, as que llegamos a: + ((+|-) T )

cte$ $

Vemos que no hay problema en la predicci on, ya que + es uno de los primeros de ((+|-) T ) . Sin embargo, al encontrarnos frente a una clausura, debemos expandirla. Es m as f acil verlo analizando c omo podemos crear el lenguaje de () . Podemos imaginar que primero se crear a algo a partir de y que despu es vendr a una nueva copia de la clausura, es decir, que nos encontraremos () . En nuestro ejemplo, tendremos que predecir (+|-) T seguido de ((+|-) T ) : cte$ + (+|-) T ((+|-) T ) $ El tope de la pila ahora es la disyunci on, por lo que tendremos que predecir alguna de sus alternativas, lo que es f acil: + + cte$ T ((+|-) T ) $

Ahora avanzamos y expandimos T normalmente: cte T $ ((+|-) T ) $

cte cte

$ ((+|-) T ) $

$ ((+|-) T ) $

Analizador sint actico

31

Nuevamente encontramos la clausura. En este caso, no tenemos uno de sus primeros en la entrada, pero s uno de sus siguientes, por lo que podemos sustituirla por la cadena vac a y llegamos a: $ $ con lo que terminamos el an alisis. 5.6.1. C alculo de primeros

Ahora debemos adaptar el algoritmo presentado en el punto 5.3 para las GPDR. La adaptaci on es sencilla: basta con a nadir los casos de la clausura y la disyunci on. Cu ales ser an los primeros de una forma sentencial () ? Por un lado, ser an los primeros de ; adem as, la clausura puede producir la cadena vac a, as que tambi en tendremos los primeros de . En cuanto a la disyunci on, tendremos que los primeros son la uni on de los primeros de cada una de las opciones. Si alguna de las opciones es anulable, habr a que mirar el resto de la forma sentencial. Teniendo esto en cuenta, el algoritmo queda: Algoritmo primeros ( : (N ) ) C= si = a entonces C := {a}; si no si = A entonces para todo A P hacer C := C primeros( ); n para si anulable( A ) entonces C := C primeros( ); n si si no si = () entonces C := primeros() primeros( ); si no si = (1 |2 | . . . |n ) entonces n C := i=1 primeros(i ); si anulable(1 |2 | . . . |n ) entonces C := C primeros( ); n si n si devolver C ; n primeros

5.6.2.

C alculo de anulables

Una modicaci on similar (gura 1) se puede hacer para el algoritmo que averigua si una forma sentencial es anulable. Basta con observar que la clausura siempre es anulable y que una disyunci on lo es si lo es alguna de sus opciones. 5.6.3. C alculo de siguientes

El c alculo de los siguientes se hace de manera similar al caso de las gram aticas incontextuales. Simplemente tendremos que aumentar las ecuaciones, teniendo en cuenta adem as que ahora tendremos que calcular los siguientes de clausuras y disyunciones. En el resto de este punto, supondremos que es un no terminal, una disyunci on o una clausura. Las ecuaciones de la p agina 23 se pueden adaptar directamente: El n de la entrada sigue al inicial: {$} siguientes( S ). Si A P , entonces: primeros( ) siguientes();
c Universitat Jaume I 2008-2009

(4)

(5)

32

II26 Procesadores de lenguaje

Algoritmo anulable ( : (N ) ) si = entonces devolver cierto; si no si = a entonces devolver falso; si no si = A entonces si existe tal que A P y anulable( ) entonces devolver anulable( ); si no devolver falso; n si si no si = () entonces devolver anulable( ); si no si = (1 |2 | . . . |n ) entonces si existe i tal que anulable(i ) entonces devolver anulable( ); si no devolver falso; n si n si n anulable
Figura 1: C alculo de anulables en GPDR.

si, adem as, anulable( ) entonces: siguientes( A ) siguientes(). (6)

Adem as debemos considerar los casos en que est e en una clausura o una disyunci on. Podemos entender mejor las ecuaciones que siguen si observamos que ( ) tiene dos posibles reescrituras: la cadena vac a y ( )( ) . En este segundo caso, lo que aparece tras es , por lo que sus primeros son siguientes de . Si es anulable, detr as de podr a aparecer cualquier cosa que pueda seguir a la clausura o, si consideramos la reescritura ( )( )( ) , los primeros de ( ). En resumen: Si ( ) aparece en la parte derecha de alguna regla, entonces: primeros( ) siguientes() y si anulable( ) entonces primeros( ) siguientes(( ) ) siguientes(). Cuando aparece en una disyunci on, el c alculo es m as sencillo: Si (1 | . . . | | . . . |n ) aparece en la parte derecha de una regla, entonces: primeros( ) siguientes() y si anulable( ) entonces siguientes((1 | . . . | | . . . |n )) siguientes(). (10) (9) (8) (7)

Si estas reglas te resultan engorrosas, siempre puedes convertir la GPDR en una gram atica incontextual y calcular sobre ella los siguientes.

Analizador sint actico

33

5.6.4.

Tabla de an alisis

La construcci on de la tabla de an alisis es an aloga a la que hac amos en el caso de las gram aticas LL(1). La u nica diferencia rese nable es que tendremos las para las clausuras y disyunciones presentes en la gram atica. Los pasos que seguimos son: Averiguar qu e no terminales y disyunciones son anulables. Calcular primeros de las partes derechas de las reglas y de los cuerpos de las clausuras y las disyunciones. Calcular siguientes de los no terminales anulables, de las clausuras y de las disyunciones anulables. Por cada regla A , poner en las celdas M [ A , a] tales que: a primeros() o anulable() y a siguientes( A ). Por cada clausura () : Introducir () en las celdas M [() , a] tales que a primeros(). Si es anulable, introducir () en las celdas M [() , a] tales que a siguientes(() ). Introducir en las celdas M [() , a] tales que a siguientes(() ). Por cada disyunci on (1 | . . . |n ): Introducir i en las celdas M [(1 | . . . |n ), a] tales que a primeros(i ). Introducir i en las celdas M [(1 | . . . |n ), a] tales que a siguientes(1 | . . . |n ) y anulable(i ). Si no hay conictos en la tabla, decimos que la GPDR es RLL(1). Vamos a calcular la tabla de an alisis de la GPDR del ejemplo: E T T ((+|-) T ) id|cte|( E )

El u nico elemento anulable es la clausura ((+|-) T ) . Necesitaremos saber los primeros de cada no terminal y de las clausuras y disyunciones: Elemento Primeros E T (+|-) ((+|-) T ) {id, cte, (} {id, cte, (} {+, -} {+, -}

Para calcular los siguientes, comenzamos con la tabla: Elemento Siguientes E T (+|-) ((+|-) T )
c Universitat Jaume I 2008-2009

{), $} {+, -} {id, cte, (}

34

II26 Procesadores de lenguaje

Extendemos los siguientes de E : Elemento E T (+|-) ((+|-) T ) Siguientes {), $} {+, -, ), $} {id, cte, (} {), $}

Y ahora ya podemos calcular la tabla (por problemas de espacio, la dibujamos en dos partes): id cte + E T (+|-) ((+|-) T ) T ((+|-) T ) id T ((+|-) T ) cte + (+|-) T ((+|-) T ) E T (+|-) ((+|-) T )
Ejercicio 28

( T ((+|-) T ) ( E )

(+|-) T ((+|-) T )

Utiliza la tabla anterior para analizar la sentencia (id + cte)-cte.

6.
6.1.

Implementaci on del analizador sint actico


Implementaci on mediante tablas

En principio, podemos implementar el analizador sint actico utilizando el algoritmo de an alisis que presentamos en 5.2. Bastar a con buscar una implementaci on adecuada de la tabla. Sin embargo, este tipo de implementaci on diculta la integraci on de acciones sem anticas junto con el an alisis, por lo que estudiaremos una implementaci on alternativa en la que la gram atica se codica directamente en forma de programa.

6.2.

Implementaci on de analizadores descendentes recursivos

Podemos reejar la tabla de an alisis (o la gram atica) directamente en un programa. La idea b asica es tener una funci on (o un m etodo si decidimos hacer que el analizador sea un objeto) por cada uno de los no terminales. As al no terminal A le asociar amos la funci on (m etodo) Analiza_A. De momento, no utilizaremos par ametros. El resultado de la llamada ser a el valor cierto si hemos encontrado en la entrada una secuencia que se pueda derivar de A y falso en caso contrario. El an alisis se har a mediante una llamada a Analiza_S: si devuelve cierto, se comprobar a que hemos llegado al nal de la entrada; si no, ha habido un error. Como en el caso del algoritmo de an alisis por tabla, utilizaremos la variable a para guardar la componente que estamos analizando en este momento. Si nuestro analizador es un objeto, podemos utilizar un atributo para a. Si el analizador es un conjunto de funciones, tendremos que hacer que a sea una variable global. En cualquier caso, antes de comenzar (antes de la llamada a Analiza_S) habr a que realizar la asignaci on a := siguiente() (l ogicamente, si el analizador l exico es un objeto, la llamada ser a algo parecido a a := alex.siguiente()). Ahora veremos una serie de esquemas que nos permitir an construir el analizador. Hemos utilizado la abreviatura aceptables para referirnos al conjunto: aceptables () = primeros(), si no anulable(), primeros() siguientes( ), si anulable(),

Analizador sint actico

35

donde se supone que estamos analizando , que ser a un no terminal, una disyunci on o una clausura. Si te das cuenta, aceptables () es el conjunto de aquellos terminales t para los cuales la celda M [, t] = . Si el no terminal A tiene las reglas A 1 , A 2 ,. . . , A n , la estructura de Analiza_A es: Funci on Analiza A opci on a aceptables A (1 ): // C odigo de 1 aceptables A (2 ): // C odigo de 2 ... aceptables A (n ): // C odigo de n si no // hemos encontrado un error devolver falso; n opci on devolver cierto; n Analiza A Las acciones de las partes derechas depender an de la forma que tengan estas partes derechas. Si = 1 2 . . . n , tendremos que realizar el reconocimiento secuencialmente: // C odigo de : // C odigo de 1 // C odigo de 2 ... // C odigo de n Si = A , tenemos que llamar a la funci on de an alisis correspondiente y comprobar que no ha habido error. La acci on queda entonces: // C odigo de A : si Analiza A()= falso entonces devolver falso // transmitimos el error n si Si = t es un terminal, tendremos que comprobar que es el que hemos le do en ese momento. En caso de que no lo sea, hemos encontrado un error: // C odigo de t si a = t entonces devolver falso // hemos encontrado un error n si a := siguiente(); La acci on correspondiente a es la acci on nula (pass en Python). Si = () , tenemos que entrar en un bucle y mantenernos en el mientras estemos en uno de los primeros de . Al salir debemos comprobar que hemos encontrado uno de los siguientes de 2 : // C odigo de () mientras a primeros(() ) hacer // C odigo de n mientras si a siguientes(() ) entonces devolver falso // hemos encontrado un error n si
2 En realidad, no es estrictamente necesario hacer esa comprobaci on. Si no se hace, el error tambi en ser a detectado, pero m as tarde, lo que puede hacer m as dif cil dar un mensaje adecuado.

c Universitat Jaume I 2008-2009

36

II26 Procesadores de lenguaje

Finalmente, si = (1 |2 | . . . |n ), tenemos que elegir la opci on adecuada entre los i : // C odigo de (1 |2 | . . . |n ) opci on a aceptables(1 |...|n ) (1 ): // C odigo de 1 aceptables(1 |...|n ) (2 ): // C odigo de 2 ... aceptables(1 |...|n ) (n ): // C odigo de n si no // hemos encontrado un error devolver falso n opci on Para la gram atica de nuestro ejemplo, el analizador ser a el que se ve en la gura 2. Este c odigo es el que obtenemos directamente de los esquemas. L ogicamente, se puede mejorar con facilidad para eliminar muchas de las comprobaciones redundantes. El resultado lo puedes ver en la gura 3.

6.3.

Tratamiento de errores

El tratamiento de errores es una de las partes de la compilaci on que m as depende del ingenio del programador. Siempre es posible encontrar mejoras a la estrategia de recuperaci on que se sigue y obtener mensajes de error m as ajustados al verdadero problema. Lo que veremos son algunas ideas generales que despu es tendr as que ajustar a tus propias necesidades. 6.3.1. Conceptos generales

Esperamos del analizador que haga algo m as que aceptar/rechazar un programa: debe informar de la presencia de errores con tanta claridad y precisi on como sea posible, y debe tratar de recuperarse del error y seguir con el an alisis para detectar otros (eventuales) errores, aprovechando as una u nica ejecuci on del compilador para detectar el mayor n umero posible de errores. Pero, en cualquier caso, la detecci on de errores y su recuperaci on no deben afectar signicativamente a la complejidad del analizador sint actico. 6.3.2. D onde se detecta un error?

Los analizadores LL(1) tienen la propiedad del prejo viable: los errores se detectan en el momento en que se ha le do el prejo m as corto de la entrada que no es v alido para ninguna cadena del lenguaje. Por lo tanto, este es el primer punto donde se puede anunciar un error analizando la entrada s mbolo a s mbolo de izquierda a derecha. El compilador debe se nalar el lugar donde se detecta el error. T picamente se indica la l nea donde se ha detectado el error y un c odigo o mensaje informativo del tipo de error. 6.3.3. Qu e acciones debemos emprender al detectar un error?

En principio, podemos pensar en abandonar la compilaci on (impl citamente, eso hemos hecho hasta ahora). Sin embargo esto no es adecuado m as que en caso de entornos muy interactivos. En general, compilar es un proceso costoso y debemos aprovechar la compilaci on para detectar tantos errores como sea posible. Otra soluci on es intentar modicar el programa fuente con la esperanza de adivinar lo que quiso escribir el programador. Generar amos entonces c odigo de acuerdo con nuestra hip otesis. Esto es muy peligroso: En general, es muy dif cil saber qu e quer a exactamente el programador.

Analizador sint actico

37

Objeto Analizador Atributos a, alex M etodo inicializaci on(al :AnalizadorL exico) alex := al ; a := alex.siguiente(); si analiza E() y a = $ entonces devolver cierto si no devolver falso n si n inicializaci on M etodo analiza E() opci on a id, cte, (: / / E T ((+|-) T ) si analiza T()=falso entonces devolver falso n si mientras a {+, -} hacer opci on a: +: si a = + entonces devolver falso n si a := alex.siguiente() -: si a = - entonces devolver falso n si a := alex.siguiente() otro: devolver falso n opci on si analiza T()= falso entonces devolver falso n si n mientras si a {), $}: devolver falso n si si no devolver falso n opci on devolver cierto n analiza E()

M etodo analiza T() opci on a: id: / / T id si a = id entonces devolver falso n si a := alex.siguiente() cte: / / T cte si a = cte entonces devolver falso n si a := alex.siguiente() (: / / T ( E ) si a = ( entonces devolver falso n si a := alex.siguiente() si analiza E()=falso entonces devolver falso n si si a = ) entonces devolver falso n si a := alex.siguiente() si no devolver falso n opci on devolver cierto n analiza T() n Analizador

Figura 2: Analizador para la gram atica de ejemplo.

c Universitat Jaume I 2008-2009

38

II26 Procesadores de lenguaje

Objeto Analizador Atributos a, F M etodo inicializaci on(al :AnalizadorL exico) alex := al ; a := alex.siguiente(); devolver analiza E() y a = $; n inicializaci on M etodo analiza E() si a {id, cte, (} entonces si analiza T()=falso entonces devolver falso n si mientras a {+, -} hacer a := alex.siguiente() si analiza T()=falso entonces devolver falso n si n mientras si a {), $}: devolver falso n si si no devolver falso n si devolver cierto n analiza E()

M etodo analiza T() opci on a: id,cte: / / T id | cte a := alex.siguiente() (: / / T ( E ) a := alex.siguiente() si analiza E()=falso entonces devolver falso n si si a = ) entonces devolver falso n si a := alex.siguiente() si no devolver falso n opci on devolver cierto n analiza T() n Analizador

Figura 3: El analizador de la gura 2 simplicado.

Puede pasar inadvertido que el c odigo generado ha sido creado a partir de un fuente con errores, lo que puede traer malas consecuencias. En el mejor de los casos, ser a una p erdida de tiempo porque tras corregirse el error se volver a a compilar. As pues, la generaci on de c odigo debe detenerse. Podemos resumir lo anterior diciendo que debemos recuperarnos el error y seguir analizando, pero sin generar c odigo. Hay diversas estrategias de recuperaci on de errores: Entrar en modo p anico: se descartan todos los componentes l exicos de entrada desde la detecci on del error hasta encontrar un componente l exico de sincronizaci on. Modelar los errores mediante producciones de error: se enriquece la gram atica con producciones que modelan los errores m as frecuentes y se asocia a estas una acci on de tratamiento de error. Tratar el error a nivel de frase: se efect ua una correcci on local, sustituyendo un prejo del resto del programa por una secuencia que permita seguir analizando. Efectuar una correcci on global: se busca el programa correcto m as pr oximo al err oneo. Las dos u ltimas opciones se pueden considerar u nicamente de inter es te orico por su elevado coste computacional y el poco benecio que dan en la pr actica.

Analizador sint actico

39

La segunda opci on tiene m as inter es, aunque obtener unas buenas reglas es bastante costoso. Un ejemplo ser a a nadir a la gram atica para las expresiones aritm eticas una regla como F ( E y despu es emitir el mensaje de error correspondiente a la falta de par entesis cuando se detectara este error. Como puedes imaginar, este tipo de reglas dan lugar a todo tipo de conictos por lo que la escritura del compilador se complica enormemente. A cambio, los mensajes de error que se pueden emitir son muy precisos y la recuperaci on muy buena. El m etodo que utilizaremos ser a el tratamiento en modo p anico. 6.3.4. Tratamiento de errores en modo p anico

Como se ha comentado arriba, el tratamiento de errores en modo p anico consiste en detener el an alisis sint actico e ir leyendo componentes l exicos hasta encontrar alg un componente que permita la sincronizaci on. Si lo consideramos desde la perspectiva del analizador, la sincronizaci on puede conseguirse de tres maneras: Podemos proseguir el an alisis como si hubi eramos encontrado la categor a l exica que esper abamos. Este es el tipo de tratamiento que har amos por ejemplo al no encontrar un punto y coma donde deber a estar. En este caso podemos sincronizarnos con los siguientes del elemento que esper abamos encontrar y con el propio elemento. Si encontramos el propio elemento al sincronizarnos, lo eliminaremos antes de continuar el an alisis. Podemos proseguir el an alisis como si hubi esemos encontrado la estructura (no terminal, clausura o disyunci on) en la que hemos encontrado el error. Esta estrategia supone sincronizarse con alguno de los siguientes de la estructura y devolver (en caso del no terminal) el control como si todo hubiera ido bien. Podr amos aplicarla, por ejemplo, si estamos intentando encontrar una expresi on en una llamada a funci on. Finalmente, podemos reintentar analizar la estructura en caso de que encontremos alguno de los primeros de la estructura que estamos analizando. En cualquier caso, el n de la entrada debe de ser siempre una de las categor as con las que nos podamos sincronizar. Para todo esto, es u til disponer de una funci on de sincronizaci on a la que se le pasa un conjunto S de categor as con las que podemos sincronizarnos. La funci on ser a: Funci on sincroniza(S {$}) S := S {$} // A nadimos el n de la entrada mientras a S hacer a := alex.siguiente() n mientras n sincroniza En nuestro ejemplo, una posible estrategia de tratamiento de errores ser a: Si no encontramos el primero de una E , hay tres posibilidades: Hemos encontrado el n de chero. Emitimos error Fin de chero inesperado y salimos. Hemos encontrado un par entesis cerrado. Emitimos error Par entesis cerrado no esperado, sincronizamos con id, cte o par entesis abierto y continuamos el an alisis. Hemos encontrado un + o un -. Emitimos error Falta operando, sincronizamos con id, cte o par entesis abierto y continuamos el an alisis. Si despu es de analizar ((+|-) T ) , no hay un par entesis ni n de chero, emitimos error Falta operador, y nos sincronizamos con id, cte o par entesis abierto (que, adem as, es lo u nico que puede haber en la entrada). Los casos para T son an alogos a los de E , salvo que estemos esperando el par entesis cerrado. Entonces, emitimos error Falta par entesis cerrado y nos sincronizamos con el o con cualquiera de los siguientes de T .
c Universitat Jaume I 2008-2009

40

II26 Procesadores de lenguaje

7.

Resumen del tema


El analizador sint actico encuentra las estructuras de la entrada. Especicamos el analizador mediante gram aticas incontextuales. El a rbol de an alisis muestra c omo derivar la entrada. Debemos trabajar con gram aticas no ambiguas o dar reglas para resolver las ambig uedades. Extendemos las gram aticas para utilizar partes derechas regulares. Dos tipos de an alisis: ascendente y descendente. Empleamos an alisis LL(1) (o RLL(1) para GPDR): En cada momento, tomamos la decisi on en base a un solo s mbolo de la entrada. No puede haber recursividad a izquierdas. Las distintas alternativas tienen que diferenciarse por sus primeros (o siguientes si son anulables). Implementaci on: mediante analizadores descendentes recursivos. Tratamiento de errores: Detiene la generaci on de c odigo. Recuperaci on en modo p anico.

También podría gustarte