09.05.2013 Views

análisis sintáctico descendente sin retroceso - Blearning

análisis sintáctico descendente sin retroceso - Blearning

análisis sintáctico descendente sin retroceso - Blearning

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Universidad de Oviedo - Departamento de Informática<br />

Escuela Politécnica Superior de Ingeniería<br />

ANÁLISIS SINTÁCTICO EN PROCESADORES DE LENGUAJE<br />

TEMA 5<br />

ANÁLISIS SINTÁCTICO DESCENDENTE<br />

Página 1


Objetivos<br />

Conocer el <strong>análisis</strong> <strong>descendente</strong> con / <strong>sin</strong> <strong>retroceso</strong><br />

Estudiar la técnica de <strong>análisis</strong> <strong>descendente</strong><br />

Establecer las condiciones LL(1)<br />

Transformar gramáticas que no cumplen la condición LL(1)<br />

Construir analizadores predictivos<br />

Tratar los errores <strong><strong>sin</strong>táctico</strong>s<br />

Página 2


Introducción<br />

Contenido<br />

El problema del <strong>retroceso</strong><br />

Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> con <strong>retroceso</strong><br />

Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s.<br />

Página 3


Introducción<br />

Clasificación métodos <strong>análisis</strong> <strong><strong>sin</strong>táctico</strong><br />

No Direccionales<br />

Direccionales<br />

A. Descendentes A. Ascendentes<br />

Algoritmo de Unger Algoritmo CYK<br />

No deterministas<br />

Predice / Concuerda<br />

1º Profundidad<br />

1º Anchura<br />

Deterministas<br />

Predice / Concuerda<br />

Gramática LL(k) - LL(1)<br />

Desplaza / Reduce<br />

1º Profundidad<br />

1º Anchura<br />

Desplaza / Reduce<br />

Gramática LR(k)<br />

LR(0), SLR(1), LALR(1)<br />

Página 4


Introducción<br />

A. Sintáctico Descendente: Características<br />

Los analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s son llamados predictivos y<br />

orientados hacia un fin, debido a la forma en que trabajan y construyen el<br />

árbol <strong><strong>sin</strong>táctico</strong>.<br />

Construyen el árbol <strong><strong>sin</strong>táctico</strong> de la sentencia a reconocer de una forma<br />

<strong>descendente</strong>, comenzando por el símbolo inicial o raíz, hasta llegar a los<br />

símbolos terminales que forman la sentencia.<br />

Los algoritmos de <strong>análisis</strong> <strong>descendente</strong> deben de cumplir al menos dos<br />

condiciones: saber en todo momento dónde se encuentra dentro del árbol<br />

<strong><strong>sin</strong>táctico</strong> y debe poder elegir la regla de producción que aplicará.<br />

Página 5


Introducción<br />

A. Sintáctico Descendente: Características<br />

Los compiladores dirigidos por <strong>sin</strong>taxis, en la forma de <strong>análisis</strong> <strong>descendente</strong><br />

recursivo fue propuesta por Lucas (1961), para describir un compilador<br />

simplificado de ALGOL 60 mediante un conjunto de subrutinas recursivas.<br />

Problema: La elegancia y comodidad de la escritura de compiladores<br />

dirigidos por <strong>sin</strong>taxis fue pagada en tiempo de compilación por el usuario.<br />

El <strong>análisis</strong> <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong>, por medio del uso de<br />

gramáticas LL(1), obtenidas por Foster (1965) y Knuth (1967). Generalizadas<br />

posteriormente por Lewis, Rosenkrantz y Stearns en 1969, dando lugar a las<br />

gramáticas LL(k).<br />

Página 6


El problema del <strong>retroceso</strong><br />

Análisis Sintáctico con <strong>retroceso</strong><br />

El problema se presenta cuando a partir del nodo raíz, el analizador<br />

<strong><strong>sin</strong>táctico</strong> no elige las producciones adecuadas para alcanzar la<br />

sentencia a reconocer.<br />

Se tienen que deshacer las producciones aplicadas hasta encontrar otras<br />

producciones alternativas, volviendo a tener que reconstruir parte del<br />

árbol <strong><strong>sin</strong>táctico</strong> (backtracking) .<br />

El <strong>retroceso</strong> puede afectar a otros módulos del compilador tales como<br />

tabla de símbolos, código generado, etc. teniendo que deshacerse<br />

también los procesos desarrollados en estos módulos.<br />

Página 7


El problema del <strong>retroceso</strong><br />

Análisis Sintáctico con <strong>retroceso</strong>: Ejemplo<br />

VN={, , }<br />

VT={module, d, p, ;, end}<br />

S=<br />

Las reglas de producción:<br />

::= module ;<br />

end<br />

::= d | d; <br />

::= p | p; <br />

Analizar la cadena de entrada: module d ; d ; p ; p end<br />

Página 8


El problema del <strong>retroceso</strong><br />

Análisis Sintáctico con <strong>retroceso</strong>: Ejemplo<br />

1. Se parte del símbolo inicial<br />

<br />

2. Aplicando la primera regla de producción de la gramática se obtiene<br />

<br />

module ; end<br />

3. Aplicando las derivaciones más a la izquierda, se tiene que:<br />

<br />

module ; end<br />

d p<br />

Página 9


El problema del <strong>retroceso</strong><br />

Análisis Sintáctico con <strong>retroceso</strong>: Ejemplo<br />

Se deriva con la segunda alternativa<br />

Se debe volver atrás<br />

<br />

module ; end<br />

d p ; <br />

<br />

module ; end<br />

d ; <br />

d<br />

p<br />

Página 10


El problema del <strong>retroceso</strong><br />

Análisis Sintáctico con <strong>retroceso</strong>: Ejemplo<br />

Se debe volver atrás<br />

<br />

module ; end<br />

d ; <br />

d<br />

p ; <br />

Los tiempos de reconocimiento de sentencias de un lenguaje pueden dispararse<br />

a causa del <strong>retroceso</strong>.<br />

p<br />

Página 11


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> con <strong>retroceso</strong><br />

Algoritmo de ASD con <strong>retroceso</strong><br />

1. Se colocan las reglas de la gramática según un orden preestablecido.<br />

2. Se comienza el árbol <strong><strong>sin</strong>táctico</strong> a partir del símbolo inicial, y se aplican<br />

las reglas en forma recursiva. Al nodo en expansión se le llama nodo activo.<br />

A → x1x2 ...xn crea n descendientes directos.<br />

A → x1 | x2 |...| xn se elegirá la alternativa de más a la izquierda.<br />

3. Si el nodo activo es un terminal se compara con el símbolo actual de la<br />

cadena. Si son iguales se avanza un token de entrada y el nuevo símbolo<br />

actual será el situado más a la derecha del terminal analizado. Si no son<br />

iguales se retrocede hasta un nodo no terminal y se reintenta eligiendo la<br />

siguiente alternativa.<br />

Si se llega al símbolo inicial la cadena no pertenece al lenguaje<br />

Página 12


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> con <strong>retroceso</strong><br />

Corolario<br />

Una gramática de contexto libre, si es una gramática limpia y no es<br />

recursiva a izquierdas, para cualquier cadena de símbolos de su alfabeto<br />

terminal existe un número finito de posibles <strong>análisis</strong> a izquierda desde el<br />

símbolo inicial para reconocerla o no.<br />

Se pueden construir analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s con <strong>retroceso</strong>. Su<br />

principal problema es el tiempo de ejecución .<br />

Página 13


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(k)<br />

Las gramáticas LL(k) son un subconjunto de las GLC.<br />

Permiten un <strong>análisis</strong> <strong>descendente</strong> determinista por medio del<br />

reconocimiento de la cadena de entrada de izquierda a derecha ("Left to<br />

right") y que va tomando las derivaciones más hacia la izquierda<br />

("Leftmost") con sólo mirar los k tokens situados a continuación de donde se<br />

halla. Si k=1 se habla de gramáticas LL(1).<br />

Las gramáticas LL(1) permiten construir un analizador determinista<br />

<strong>descendente</strong> con tan sólo examinar en cada momento el símbolo actual de la<br />

cadena de entrada para saber que producción aplicar.<br />

Página 14


Teorema<br />

S-gramáticas<br />

Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(k)<br />

Una gramática LL(k) es no ambigua<br />

Una gramática LL(k) no es recursiva a izquierdas<br />

Son un subconjunto muy restringido de las gramáticas LL(1)<br />

Debe cumplir las siguientes dos condiciones:<br />

1. Todas las partes derechas de cada producción comienzan con un símbolo<br />

terminal.<br />

2. Si dos producciones tienen la misma parte izquierda, entonces su parte<br />

derecha comienza con diferentes símbolos terminales.<br />

A: A → a 1 α 1 | a 2 α 2 | ...| a m α m<br />

se debe cumplir que:<br />

a i ≠ a j ∀ i ≠ j a i ∈VT α i ∈V * 1 ≤ i ≤ m<br />

Página 15


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(k)<br />

Corolario de la definición de S-gramática<br />

Ejemplos:<br />

Toda S-gramática es LL(1), la inversa no es cierta.<br />

S → a T<br />

S → T b S<br />

T → b T<br />

T → b a<br />

No es S-gramática<br />

S → a b R<br />

S → b R b S<br />

R → a<br />

R → b R<br />

S → p X<br />

S → q Y<br />

X → a X b<br />

X → x<br />

Y → a Y d<br />

Y → y<br />

Página 16


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Conjunto de símbolos INICIALES<br />

Se define el conjunto de símbolos iniciales o cabecera de un símbolo<br />

α∈(VT ∪ VN) * , como el conjunto de símbolos terminales que pueden<br />

aparecer al principio de cadenas derivadas de α. La definición anterior se<br />

puede expresar como:<br />

INICIALES(α) = {a / α ⇒ a ... siendo a ∈ VT }<br />

*<br />

Si α ⇒ a σ1 ...σn entonces {a} ∈ INICIALES(α) con a ∈ VT<br />

*<br />

Si α ⇒ λ entonces {λ} ∈ INICIALES(α)<br />

Página 17


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Algoritmo para calcular el conjunto de iniciales<br />

Repetir hasta que no se puedan añadir más símbolos terminales o λ al conjunto<br />

1. Si X es un terminal o λ, entonces INICIALES (X) = {X}<br />

2. Si X es un no-terminal, entonces para cada producción de la forma<br />

X→X 1 X 2 ...X n , INICIALES (X) contiene a INICIALES (X 1 ) – {λ}. Si<br />

también para algún i < n todos los conjuntos INICIALES (X 1 ) ...<br />

INICIALES (X i ) contienen a λ, entonces INICIALES (X) contiene a<br />

INICIALES (X i+1 ) – {λ}. Si todos los conjuntos INICIALES (X 1 ) ...<br />

INICIALES (Xn) contiene λ, entonces INICIALES (X) también contiene a<br />

λ.<br />

Página 18


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Algoritmo para calcular el conjunto de iniciales<br />

3. Se define INICIALES (α), para cualquier cadena α = X 1 X 2 ...X n , de<br />

terminales y no-terminales, de la manera siguiente. INICIALES (α) contiene a<br />

INICIALES (X 1 ) – {λ}. Para cada i = 2, ...,n si INICIALES (X k ) contiene a λ<br />

para toda k = 1, ...i-1, entonces INICIALES(α) contiene a INICIALES (X i ) -<br />

{λ}. Finalmente, si para todo i = 1...n, INICIALES (X i ) contiene λ, entonces<br />

INICIALES (α) contiene a λ.<br />

Página 19


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Seudocódigo del algoritmo para todo no terminal A<br />

for todo no terminal A do INICIALES(A) := {}<br />

while existan cambios en cualquier INICIALES(A) do<br />

for cada selección de producción A→X1X2 ...Xn do<br />

k :=1; continuar = verdadero;<br />

while contiuar = verdadero and k


Ejemplos:<br />

S → A B e<br />

A → d B<br />

A → a S<br />

A → c<br />

B → A S<br />

B → b<br />

Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Conjunto de Símbolos Iniciales<br />

exp → term exp´<br />

exp´ → opsuma term exp´ | λ<br />

opsuma → + | -<br />

term → factor term´<br />

term´ → opmult factor term´ | λ<br />

opmult → *<br />

factor → ( exp ) | numero<br />

INICIALES (A) = {d, a, c}<br />

INICIALES (S) = INICIALES(A)={d, a, c}<br />

INICIALES (B) = INICIALES(A) ∪ {b} ={d, a, c, b}<br />

INICIALES (exp) = {(, numero}<br />

INICIALES (exp´) = {+,-, λ}<br />

INICIALES (opsuma) = {+, -}<br />

INICIALES (term) = {(, numero}<br />

INICIALES (term´) = {*, λ}<br />

INICIALES (opmult) = {*}<br />

INICIALES (factor) = {(, numero}<br />

Página 21


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Gramáticas LL(1) simples<br />

Las gramáticas LL(1) simples son un subconjunto de las gramáticas LL(1), con las<br />

dos restricciones siguientes:<br />

1. No se permiten símbolos no terminales que deriven a vacío.<br />

2. Las distintas producciones de cada no terminal A ∈ VN A → α 1 | α 2 | ...| α n<br />

deben cumplir que los conjuntos INICIALES(α 1 ), INICIALES(α 2 ),...,<br />

INICIALES(α n ) sean disjuntos entre sí.<br />

INICIALES(α i ) ∩ INICIALES(α j ) = ∅ ∀ i ≠ j<br />

Página 22


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Corolario de las gramáticas LL(1) simples<br />

Toda gramática LL(1) simple es LL(1), lo contrario no es cierto.<br />

Teorema de equivalencia entre gramáticas LL(1) y S-gramáticas<br />

Dada una gramática LL(1) simple siempre es posible encontrar una<br />

S-gramática equivalente.<br />

Ejemplo: S → A B e<br />

A → d B<br />

A → a S<br />

A → c<br />

B → A S<br />

B → b<br />

Página 23


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Conjunto de símbolos SEGUIDORES<br />

Se define el conjunto de símbolos seguidores o siguientes para un símbolo no<br />

terminal A como el conjunto de símbolos terminales que en cualquier<br />

momento de la derivación pueden aparecer inmediatamente a la derecha de (o<br />

después de) A<br />

SEGUIDORES (A) = { a / S ⇒ βAaδ con β, δ∈ (VT ∪ VN) * }<br />

Página 24


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Algoritmo para calcular el conjunto de Seguidores<br />

Repetir hasta que no cambie el conjunto de seguidores<br />

1. Si A es el símbolo inicial, entonces $ está en SEGUIDORES (A).<br />

2. Si hay una producción B →αAγ, entonces<br />

INICIALES (γ) – {λ} ∈ SEGUIDORES (A)<br />

3. Si existe una producción B →αA ó B →αAγ tal que λ∈INICIALES (γ)<br />

entonces SEGUIDORES (B) ⊂ SEGUIDORES (A)<br />

Página 25


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Seudocódigo del algoritmo para el cálculo de conjuntos de Seguidores<br />

SEGUIDORES (símbolo-inicial) := {$};<br />

for todos los no terminales A ≠ símbolo-inicial do SEGUIDORES(A):={};<br />

while existan cambios en cualquier conjunto SEGUIDORES do<br />

for cada producción A→X 1 X 2 ...X n do<br />

for cada X i que sea un no terminal do<br />

añadir INICIALES (X i+1 X i+2 ...X n ) - {λ} a SEGUIDORES (X i )<br />

(* NOTA: si i=n, entonces X i+1 X i+2 ...X n = λ *)<br />

if λ está en INICIALES(X i+1 X i+2 ...X n ) then<br />

añadir SIGUIENTE (A) a SIGUIENTE (X i )<br />

Página 26


Ejemplos:<br />

Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Conjunto de Símbolos Seguidores<br />

sentencia → sent-if | otra<br />

sent-if → if (exp) sentencia parte-else<br />

parte-else → else sentencia | λ<br />

exp → 0 | 1 SEGUIDORES (sentencia) = {$, else}<br />

SEGUIDORES (sent-if) = {$, else}<br />

SEGUIDORES (parte-else) = {$, else}<br />

SEGUIDORES (exp) = {)}<br />

exp → term exp´<br />

exp´ → opsuma term exp´ | λ<br />

opsuma → + | -<br />

term → factor term´<br />

term´ → opmult factor term´ | λ<br />

opmult → *<br />

factor → ( exp ) | numero<br />

SEGUIDORES (exp) = {$, )}<br />

SEGUIDORES (exp´) = {$, )}<br />

SEGUIDORES (opsuma) = {(, numero}<br />

SEGUIDORES (term) = {$, ), +, -}<br />

SEGUIDORES (term´) = {$, ), +, -}<br />

SEGUIDORES (opmult) = {(, numero}<br />

SEGUIDORES (factor) = {$, ), +,-,*}<br />

Página 27


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

Conjunto de símbolos DIRECTORES<br />

Los símbolos directores de una producción A → αson los que dirigen al<br />

analizador <strong><strong>sin</strong>táctico</strong> para elegir la alternativa adecuada.<br />

Se definen como el conjunto de símbolos terminales que determinarán que<br />

expansión de un no terminal se ha de elegir en un momento dado, con solo<br />

mirar un símbolo hacia delante.<br />

SD (A, α )<br />

INICIALES (α) si α es no anulable<br />

INICIALES (α) ∪ SEGUIDORES (A) si α es anulable<br />

Página 28


Definición<br />

Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Gramáticas LL(1)<br />

La condición necesaria y suficiente para que una gramática limpia sea LL(1),<br />

es que los símbolos directores correspondientes a las diferentes expansiones<br />

de cada símbolo no terminal sean conjuntos disjuntos.<br />

La condición es necesaria, puesto que si un símbolo aparece en dos<br />

conjuntos de símbolos directores, el analizador <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> no<br />

puede decidir que expansión aplicar.<br />

La condición es suficiente, puesto que el analizador siempre puede escoger<br />

una alternativa correcta. Si el símbolo no está contenido en ninguno de los<br />

conjuntos, la cadena de entrada no pertenece al lenguaje.<br />

Página 29


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Condiciones de las gramáticas LL(1)<br />

Primera condición de Knuth<br />

No se permitirán producciones de la forma A → A α donde A ∈ a VN y α ∈ V * .<br />

Esta condición equivale a no admitir la recursividad a izquierdas.<br />

Segunda condición de Knuth<br />

Los símbolos terminales que pueden encabezar las distintas alternativas de una<br />

regla de producción deben formar conjuntos disjuntos.<br />

No debe ocurrir que:<br />

A → Bβ | Cγ A,B,C ∈ VN<br />

β, γ ∈ V *<br />

*<br />

*<br />

B → dS d ∈ VT<br />

C → dψ S, ψ ∈ V *<br />

*<br />

Página 30


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Condiciones de las gramáticas LL(1)<br />

Tercera condición de Knuth<br />

Si una alternativa de un símbolo no terminal origina la cadena vacía, entonces:<br />

(INICIALES (A) ∩ SEGUIDORES(A) = ∅ )<br />

Sea la cadena A 1 ... A 2 ... A 3 A 4 A 5 y sea A 3 el símbolo que se está analizando,<br />

además se tienen las producciones:<br />

A 3 →ax | λ<br />

A 4 →A 3 ay<br />

Dado que A3 puede derivar a la cadena vacía, puede darse el caso de que:<br />

INICIALES(A3 )= { a }<br />

INICIALES(A4 )= { a }<br />

y no puede determinarse si se ha de elegir la producción de A3 o de A4 Página 31


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Condiciones de las gramáticas LL(1)<br />

Cuarta condición de Knuth<br />

Ningún símbolo no terminal puede tener dos o más alternativas que conduzcan<br />

a la cadena vacía. Esta condición deriva de la anterior.<br />

Así por ejemplo no se permite .<br />

X → A | B<br />

A → λ | C<br />

B → λ | D<br />

Página 32


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

No se puede saber si un determinado lenguaje puede<br />

ser generado o no por una gramática LL(1), hasta que<br />

no se encuentre esta gramática.<br />

No existe ningún algoritmo general que transforme<br />

una gramática a LL(1).<br />

En algunos casos, se puede obtener una gramática<br />

equivalente por medio de las transformaciones.<br />

GRAMÁTICAS LIBRES<br />

DE CONTEXTO<br />

NO AMBIGUAS<br />

LL(1)<br />

Página 33


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

Eliminación de la recursividad a izquierdas<br />

Se dice que una gramática tiene recursividad a izquierdas, si existe un no terminal<br />

A, tal que para algún α ∈ V * existe una derivación de la forma:<br />

EJEMPLO<br />

S → aAc<br />

A → Ab | λ<br />

Reconocer la cadena abbc<br />

(1)<br />

S<br />

Α ⎯ ⎯→ Α α<br />

+<br />

(2)<br />

S<br />

a A c<br />

(3)<br />

S<br />

a A c<br />

A b<br />

(4)<br />

S<br />

a A c<br />

A b<br />

A b<br />

S<br />

a A c<br />

A b<br />

A b<br />

A b<br />

(5)<br />

Página 34


1ª) Transformación<br />

S → aAc<br />

A → λ | Ab<br />

2ª) Transformación<br />

S → aAc<br />

A → λ | bA<br />

Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

(1)<br />

S<br />

(1)<br />

S<br />

(2)<br />

S<br />

a A c<br />

(2)<br />

S<br />

a A c<br />

(3)<br />

S<br />

a A c<br />

(3)<br />

S<br />

a A c<br />

b A<br />

(4)<br />

S<br />

a A c<br />

(4)<br />

S<br />

A b<br />

a A c<br />

b A<br />

b A<br />

(5)<br />

S<br />

a A c<br />

A b<br />

A b<br />

(5)<br />

S<br />

a A c<br />

b A<br />

b A<br />

Página 35


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

Eliminación de la recursividad a izquierdas directa<br />

Se sustituye por:<br />

Caso general<br />

β i no comienza por A<br />

A → A α ⏐ β<br />

A → β C<br />

C → αC ⏐ λ<br />

A<br />

A<br />

A<br />

Bucle infinito<br />

A → A α 1 | A α 2 | ... | A α n | β 1 | β 2 | β 3 | ... | β n<br />

A → β 1 C | β 2 C | ... | β n C<br />

C → α 1 C | α 2 C | ... | α n C | λ<br />

A<br />

ß<br />

A<br />

C<br />

C<br />

C<br />

Página 36


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

Eliminación de la recursividad a izquierdas indirecta<br />

El método para resolver las recursividades indirectas es convertirlas a<br />

recursividades directas por medio de sustituciones.<br />

Ejemplo:<br />

::= .<br />

::= ;<br />

::= return | end<br />

::= exit<br />

Gramática equivalente<br />

::= .<br />

::= ;<br />

::= return | end<br />

::= end ; exit <br />

::= return ; exit | λ<br />

::= exit<br />

::= ; exit<br />

::= return ; exit<br />

::= end ; exit<br />

Página 37


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

Factorización y sustitución<br />

Trata de agrupar las producciones que comienzan por el mismo símbolo no anulable,<br />

realizar sustituciones de reglas o incluir nuevos símbolos no terminales.<br />

Algoritmo A → αβ 1 | αβ 2 | ... αβn|γ<br />

Primer paso: para cada no terminal A buscar el prefijo más largo común a<br />

dos o más alternativas de dicho no terminal.<br />

Segundo paso: Si α≠ε, sustituir todas las producciones de A por:<br />

A → α C | γ<br />

C → β 1 | β 2 |...|β n<br />

Página 38


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

Ejemplo: Factorización y sustitución<br />

::= + |<br />

- |<br />

<br />

::= * |<br />

/ |<br />

<br />

::= ^ |<br />

<br />

::= - |<br />

<br />

::= ( ) |<br />

identificador |<br />

constante<br />

Aplicando factorización y sustitución<br />

::= + | - <br />

::= * | / <br />

::= ^ | <br />

::= | <br />

::= |<br />

<br />

::= <br />

::= - |<br />

<br />

::= ( ) |<br />

identificador | constante<br />

Página 39


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

::= <br />

::= + <br />

::= - <br />

::= <br />

::= <br />

::= * <br />

::= / <br />

::= <br />

::= <br />

::= ^ <br />

::= <br />

::= - <br />

::= <br />

::= ( )<br />

::= identificador<br />

::= constante<br />

::= λ<br />

Página 40


Análisis <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> <strong>sin</strong> <strong>retroceso</strong><br />

Transformación de gramáticas<br />

Transformación mediante aspectos semánticos<br />

En algunos lenguajes de programación es necesario conocer más información<br />

que la estrictamente <strong>sin</strong>táctica, para elegir el símbolo director en cada momento .<br />

Ejemplo:<br />

::= begin end<br />

(1) aa: x:= 5<br />

::= <br />

::= identificador :<br />

| λ<br />

(2) yy:= 5<br />

::= while DO <br />

| := <br />

| repeat until <br />

| for := do <br />

| goto constante<br />

| case of end<br />

...<br />

::= identificador<br />

...<br />

Página 41


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Técnicas de construcción<br />

Métodos basados directamente en la <strong>sin</strong>taxis.<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s recursivos.<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s dirigidos por tabla.<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s basados en máquinas<br />

de tipo 2 o de pila.<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s dirigidos por estructuras de datos.<br />

Página 42


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Métodos basados directamente en la <strong>sin</strong>taxis<br />

Reglas de construcción de diagramas <strong><strong>sin</strong>táctico</strong>s<br />

Operación BNF Diagrama Conway<br />

Secuencial AB<br />

Alternativa<br />

Repetitiva<br />

A | B<br />

λ | B<br />

{B} 1 o más<br />

[B] 0 o más<br />

A B<br />

A<br />

B<br />

B<br />

B<br />

B<br />

Métodos sencillos para dar<br />

los primeros pasos entre<br />

teoría e implementación.<br />

Toda gramática reconocible<br />

mediante el método de los<br />

diagramas de Conway es<br />

LL(1).<br />

Página 43


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Métodos basados directamente en la <strong>sin</strong>taxis<br />

Traducción de reglas <strong>sin</strong>tácticas a programas<br />

Los símbolos no terminales son procedimientos, funciones o métodos.<br />

Los símbolos terminales son tokens enviados por el analizador léxico.<br />

Las reglas de producción se traducen a estructuras de control.<br />

Página 44


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Métodos basados directamente en la <strong>sin</strong>taxis<br />

Ejemplo 1: Factor<br />

La regla de producción A → S 1 | S 2 | S 3 | ... | S n se traduce a una sentencia multialternativa<br />

factor ( ){<br />

switch (token) {<br />

case ID : get_token ( ); break;<br />

case NUM : get_token ( ); break;<br />

case NOT : get_token ( ); factor ( ); break;<br />

case AB_PAR : get_token ( ); expresion ( );<br />

if (token != CE_PAR)<br />

{Error: Paréntesis de cierre}<br />

else get_token ( );<br />

break;<br />

default : Error : Expresión no válida.<br />

}<br />

}<br />

donde token ∈ Símbolos Directores (A, S 1 | S 2 | ... | S n )<br />

identificador<br />

numero<br />

NOT<br />

factor<br />

( expresion )<br />

Página 45


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Métodos basados directamente en la <strong>sin</strong>taxis<br />

Ejemplo 2: Expresión-simple<br />

La regla de producción A → SA | λ o A → SA | S se traduce a una sentencia repetitiva<br />

+<br />

-<br />

termino<br />

expr_simple ( ) {<br />

OR<br />

if ((token == MAS) || (token == MENOS)) {<br />

get_token( );<br />

}<br />

termino ( );<br />

while ((token == MAS) || (token == MENOS) || (token == OR)) {<br />

get_token( );<br />

termino ( );<br />

}<br />

}<br />

+<br />

-<br />

termino<br />

Página 46


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s recursivos<br />

La condición necesaria para que un analizador <strong>descendente</strong> recursivo<br />

opere correctamente es que la gramática del lenguaje fuente sea LL(1).<br />

Se considera a cada regla de la gramática como la definición de una<br />

función o método que reconocerá al no terminal de la parte izquierda.<br />

El lado derecho de la regla especifica la estructura del código para ese<br />

método o función.<br />

Los símbolos terminales corresponden a concordancias con la entrada.<br />

Los símbolos no terminales con llamadas a funciones o métodos.<br />

Las diferentes alternativas a casos condicionales en función de lo que<br />

se esté examinando en la entrada.<br />

Página 47


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s recursivos<br />

Implementación de un analizador <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> recursivo<br />

Una función denominada Match<br />

Funcion Match(terminal)<br />

inicio<br />

si (token-actual == terminal) entonces<br />

obtener siguiente token<br />

<strong>sin</strong>o<br />

error <strong><strong>sin</strong>táctico</strong><br />

fin<br />

Una función para cada no terminal con la siguiente estructura:<br />

Para las reglas de la forma A → α 1 | α 2 | ... | α n decidir la producción<br />

a utilizar en función de los conjuntos INICIALES(α i ). .<br />

Página 48


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s recursivos <strong>descendente</strong>s<br />

Funcion A()<br />

inicio<br />

segun token-actual está en:<br />

INICIALES(α 1 ): { proceder según alternativa α 1 }<br />

INICIALES(α 2 ): { proceder según alternativa α 2 }<br />

...<br />

INICIALES(α n ): { proceder según alternativa α n }<br />

Fin-segun<br />

si token-actual no pertenece a ningún INICIALES (α n )<br />

entonces<br />

Fin<br />

error <strong><strong>sin</strong>táctico</strong>, excepto si existe la alternativa<br />

A → ε en cuyo caso no se hace nada.<br />

Página 49


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s recursivos<br />

Para cada alternativa α i del no terminal, proceder analizando<br />

secuencialmente cada uno de los síımbolos que aparece en la parte derecha.<br />

Si es un no terminal entonces<br />

hacer una llamada a su función<br />

Si es un terminal entonces<br />

hacer una llamada a la función Match con ese terminal<br />

como argumento.<br />

Para lanzar el analizador <strong><strong>sin</strong>táctico</strong> se hace una llamada a la función<br />

asociada al símbolo inicial de la gramática. No olvidar hacer una llamada<br />

previa al analizador léxico para inicializar la variable con el primer token<br />

del fichero de entrada.<br />

Página 50


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s recursivos<br />

Ejemplo: Construir un ASD recursivo para la siguiente gramática<br />

instruccion → identificador = exp ;<br />

exp → termino mastermino<br />

mastermino → + termino mastermino| λ<br />

termino → identificador | constante<br />

#include<br />

#include"tokens.h“<br />

int tokenActual;<br />

... /* otras declaraciones */<br />

void main(){<br />

tokenActual=yylex();<br />

instruccion();<br />

}<br />

SDescendente.c<br />

Página 51


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s recursivos<br />

void terminal(int token, char *mensCasoError){<br />

if(tokenActual==token)<br />

tokenActual=yylex();<br />

else hayError(mensCasoError);<br />

instruccion →<br />

}<br />

identificador = exp ;<br />

void instruccion(){<br />

terminal(IDENTIFICADOR,”Se esperaba ID”);<br />

terminal(ASIGNACION,”Se esperaba ‘=‘”);<br />

expresion();<br />

terminal((int) ‘;’,”Se esperaba ‘;’”);<br />

}<br />

void expresion(){ exp → termino<br />

termino();<br />

mastermino<br />

mastermino();<br />

}<br />

Página 52<br />

SDescendente.c


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s recursivos<br />

void termino(){<br />

if(tokenActual==IDENTIFICADOR)<br />

terminal(IDENTIFICADOR, "Se esperaba ID"); termino →<br />

else if(tokenActual==CONSTANTE {<br />

identificador |<br />

terminal(CONSTANTE, "Se esperaba CTE"); constante<br />

else hayError("Se esperaba un ID o CTE");<br />

}<br />

void mastermino(){<br />

mastermino → + termino<br />

mastermino | λ<br />

if(tokenActual=='+'){<br />

terminal((int) ‘+‘,”Se esperaba ‘+‘”);<br />

termino();<br />

mastermino();<br />

} /* el else: corresponde a la producción λ */}<br />

/* ... funcion hayError */<br />

SDescendente.c<br />

Página 53


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s dirigidos por tabla<br />

El hecho de que haya reglas recursivas hace que el analizador predictivo<br />

implementado directamente sea recursivo. Sin embargo, la recursividad se puede<br />

evitar mediante el uso explícito de una pila.<br />

Esquema general<br />

Entrada<br />

... b c a...$<br />

Matriz bidimensional.<br />

Es lo único que cambia<br />

de un analizador a otro.<br />

Analizador<br />

Sintáctico<br />

Tabla de <strong>análisis</strong><br />

M[X,a]<br />

Z<br />

Y<br />

K<br />

$<br />

Pila de símbolos<br />

(a reconocer)<br />

Salida<br />

A → X<br />

X → Z Y k<br />

Producciones utilizadas en<br />

el <strong>análisis</strong> de la secuencia<br />

de entrada.<br />

.<br />

Página 54


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s dirigidos por tabla<br />

Construcción de la tabla de <strong>análisis</strong> <strong><strong>sin</strong>táctico</strong><br />

Entrada: una gramática G<br />

Salida: la tabla de <strong>análisis</strong> <strong><strong>sin</strong>táctico</strong>, con una fila para cada<br />

no-terminal, una columna para cada terminal y otra para $<br />

Método:<br />

Ampliar la gramática con una producción S’→ S$<br />

Para cada producción de la gramática A → α hacer:<br />

– Para cada terminal a ∈ PRIMEROS(α), añadir la producción A → α<br />

en la casilla M[A,a].<br />

– Si λ ∈ PRIMEROS(α), añadir A → α en la casilla M[A,b]<br />

∀b ∈ SIGUIENTES(A).<br />

Las celdas de la tabla que hayan quedado vacías se definen como<br />

error.<br />

Las gramáticas LL(1) garantizan que solo aparezca una producción por casilla<br />

Página 55


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s dirigidos por tabla<br />

Ejemplo:<br />

E ::= T E’<br />

E’ ::= + T E’ | λ<br />

T ::= F T’<br />

T’ ::= * F T’ | λ<br />

F ::= ( E ) | Id<br />

INIC (T E’) = {(, id}<br />

INIC (+T E’) = {+}<br />

INIC (λ ) = {λ}<br />

INIC (F T’) = {(,id}<br />

INIC (* F T’) = {*}<br />

INIC ( ( E ) ) = {(}<br />

INICIALES (id) = {id}<br />

E<br />

E’<br />

T<br />

T’<br />

F<br />

SEG (E’) = {$, )}<br />

SEG (T’) = {+,$,)}<br />

id<br />

TE’<br />

FT’<br />

id<br />

+<br />

+TE’<br />

λ<br />

*<br />

*FT’<br />

(<br />

TE’<br />

FT’<br />

(E)<br />

Tabla de <strong>análisis</strong> <strong><strong>sin</strong>táctico</strong><br />

)<br />

λ<br />

λ<br />

$<br />

λ<br />

λ<br />

Página 56


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s dirigidos por tabla<br />

Pila Entrada Producción<br />

$ E<br />

$ E’ T<br />

$ E’ T’ F<br />

$ E’ T’ Id<br />

$ E’ T’<br />

$ E’ T’ F *<br />

$ E’ T’ F<br />

$ E’ T’ Id<br />

$ E’ T’<br />

$ E’<br />

$ E’ T +<br />

$ E’ T<br />

$ E’ T’ F<br />

$ E’ T’ Id<br />

$ E’ T’<br />

$ E’<br />

$<br />

Id * Id + Id $<br />

Id * Id + Id $<br />

Id * Id + Id $<br />

Id * Id + Id $<br />

* Id + Id $<br />

* Id + Id $<br />

Id + Id $<br />

Id + Id $<br />

+ Id $<br />

+ Id $<br />

+ Id $<br />

Id $<br />

Id $<br />

Id $<br />

$<br />

$<br />

$<br />

E::= T E’<br />

T::= F T’<br />

F::= Id<br />

T’::= * F T’<br />

F::= Id<br />

T’::= λ<br />

E’::= + T E’<br />

T::= F ⋅ T’<br />

F::= Id<br />

T’::= λ<br />

E’::= λ<br />

Página 57


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s basados en AP<br />

Si la gramática es LL(1) el autómata de pila será determinista.<br />

Entrada: La cadena w$ a reconocer y una<br />

gramática G.<br />

Salida: Si w$ ∈ L(G), la derivación más a la<br />

izquierda de la cadena de entrada, <strong>sin</strong>o una<br />

indicación de error.<br />

Método: Como configuración inicial se tiene<br />

en el fondo de la pila el símbolo $, el símbolo<br />

inicial de la gramática S en la cima y la cadena<br />

w$ en el buffer de entrada.<br />

CONTROL<br />

DE<br />

ESTADOS<br />

entrada<br />

token1 token2 token3 ... tokenN-1 tokenN<br />

cabeza de<br />

lectura<br />

tope<br />

Z<br />

Y<br />

X<br />

.<br />

A<br />

$<br />

Pila<br />

apila<br />

desapila<br />

Página 58


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s basados en AP<br />

Algoritmo para el <strong>análisis</strong> <strong><strong>sin</strong>táctico</strong> <strong>descendente</strong> determinista<br />

REPETIR<br />

SEGÚN sea el símbolo del tope de la pila<br />

Terminal: Si el símbolo de entrada (token) coincide con el terminal entonces<br />

Coger siguiente token<br />

Extraer el símbolo de la pila<br />

Sino<br />

Error<br />

No-Terminal: Utilizar el símbolo no-terminal y el símbolo de entrada (token)<br />

para determinar la producción correspondiente<br />

Si se encuentra la producción X → Y1Y2 ... Yk entonces<br />

Extraer el símbolo no-terminal X<br />

Introducir Yk, Yk-1 , ... Y1 en la pila<br />

(Y1 estaría en el tope de la pila)<br />

Sino<br />

Error<br />

HASTA (Pila vacía o error)<br />

Si la pila está vacía entonces<br />

Aceptar la cadena de entrada<br />

Sino<br />

Error<br />

Página 59


Construcción de analizadores <strong><strong>sin</strong>táctico</strong>s <strong>descendente</strong>s<br />

Analizadores <strong><strong>sin</strong>táctico</strong>s dirigidos por ED<br />

Se supone que una gramática está formada por un conjunto determinista de<br />

grafos <strong><strong>sin</strong>táctico</strong>s.<br />

Cada nodo se puede representar por:<br />

Símbolo<br />

Secuencial<br />

- Símbolo terminal<br />

- Símbolo no terminal → puntero a la estructura<br />

de datos que representa el símbolo.<br />

S 1 S 2 S 3 S n<br />

Alternativa Sucesor<br />

NIL<br />

S 1<br />

S 2<br />

S n<br />

Alternativa<br />

S 3<br />

Fueron propuestos por<br />

Wirth con el objetivo<br />

de construir<br />

analizadores genéricos<br />

Repetitiva<br />

S<br />

vacío<br />

NIL<br />

Página 60


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Los errores en programación pueden ser:<br />

Léxicos: producidos al escribir mal un identificador, una palabra clave, un<br />

operador, etc<br />

Sintácticos: cuando la secuencia de tokens enviados por el analizador léxico<br />

no son reconocidos por la gramática que describe el lenguaje. Pueden ser<br />

producidos por una expresión aritmética o paréntesis no equilibrados, etc.<br />

Semánticos: producidos como consecuencia de la aplicación de un operador a<br />

un operando incompatible, etc.<br />

Página 61


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Un gestor de errores debe proporcionar las siguientes funciones:<br />

Determinar si el programa es <strong>sin</strong>tácticamente correcto.<br />

Proporcionar un mensaje de error significativo:<br />

parser error: linea 10 columna 4, simbolo encontrado f símbolo esperado ;<br />

Declarar que ha ocurrido un error tan pronto como sea posible. Esperar demasiado<br />

tiempo significa que la ubicación real del error puede haberse perdido.<br />

Reanudar el <strong>análisis</strong> tan pronto como sea posible. Debería intentar analizar tanto código<br />

como fuera posible para detectar los errores reales.<br />

Evitar errores en cascada. Un error genera una secuencia de mensajes de error falsos.<br />

Evitar bucles infinitos en los que se genera una cascada <strong>sin</strong> fin de mensajes de error.<br />

Para evitarlo se debe ignorar parte de la entrada.<br />

Realizar una reparación del error. El analizador intenta inferir un programa correcto de<br />

uno incorrecto.<br />

Página 62


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Recuperación de errores<br />

Los métodos de recuperación de errores suelen ser métodos “ad-hoc”, en el<br />

sentido de que se aplican a lenguajes específicos y a métodos concretos de <strong>análisis</strong><br />

Sintáctico (<strong>descendente</strong>, ascendente. etc), con muchas situaciones particulares.<br />

Existen varias estrategias para corregir errores, una vez detectados<br />

Recuperación en modo de alarma (Panic mode ): Consiste en ignorar el<br />

resto de la entrada hasta llegar a una condición de seguridad, un token<br />

especial (por ejemplo un ‘;’ o un ‘END’).<br />

Error<br />

id ‘=’ id ‘[‘ id ‘]’ id ’[‘ id ‘]’ ’=’ id ’[‘ id ‘]’ ‘;’ id ’[‘ id ‘]’ ‘=’ id ‘;’<br />

Token especial utilizado para continuar<br />

la compilación a partir de él<br />

Ejemplo:<br />

aux = a[i]<br />

a[i] = a[j];<br />

a[j] = aux;<br />

Página 63


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Recuperación de errores<br />

Recuperación a nivel de frase: Intenta recuperar el error una vez<br />

descubierto. En el caso anterior, por ejemplo, podría haber sido lo<br />

suficientemente inteligente como para insertar el token ‘;’. Hay que tener<br />

cuidado con este método ya que caben varias posibilidades.<br />

Reglas de producción adicionales para el control de errores:La gramática<br />

se puede aumentar con las reglas que reconocen los errores más comunes.<br />

sent_errónea → sent_<strong>sin</strong>_acabar sentencia_acabada<br />

sentencia_acabada → sentencia ‘;’<br />

sent_<strong>sin</strong>_acabar → sentencia<br />

Corrección Global: el analizador <strong><strong>sin</strong>táctico</strong> le pide toda la secuencia de<br />

tokens al léxico, y lo que hace es devolver lo más parecido a la cadena de<br />

entrada pero <strong>sin</strong> errores, así como el árbol que lo reconoce.<br />

Página 64


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Recuperación de errores en ASD recursivos<br />

Una forma estándar de recuperación de errores en los ASD recursivos se<br />

denomina Modo de alarma.<br />

Consiste en proporcionar a cada procedimiento (cada no-terminal de la<br />

gramática) un parámetro extra, un conjunto de tokens de <strong>sin</strong>cronización.<br />

Según se va efectuando el <strong>análisis</strong>, los tokens que pueden funcionar como<br />

tokens de <strong>sin</strong>cronización se agregan según se vayan realizando las llamadas.<br />

Si se encuentra un error, el analizador explora hacia delante, desechando<br />

tokens hasta encontrar uno que pertenezca al conjunto reanudándose así el<br />

<strong>análisis</strong>.<br />

Las cascadas de error se evitan al no generar nuevos errores mientras tiene<br />

lugar esta exploración.<br />

Página 65


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Recuperación de errores en ASD recursivos<br />

¿ Qué tokens se agregan al conjunto de <strong>sin</strong>cronización en cada punto<br />

del <strong>análisis</strong> <strong><strong>sin</strong>táctico</strong> ?<br />

Los conjuntos de SEGUIDORES<br />

Los conjuntos de INICIALES para evitar que el manejador de errores omita<br />

tokens importantes que inicien nuevas construcciones (sentencias, expresiones)<br />

Ejemplo: Sea la gramática de expresiones:<br />

exp → term exp´<br />

exp´ → opsuma term exp´ | λ<br />

opsuma → + | -<br />

term → factor term´<br />

term´ → opmult factor term´ | λ<br />

opmult → *<br />

factor → ( exp ) | numero<br />

Página 66


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Recuperación de errores en ASD recursivos<br />

Además del procediemiento Match y los correspondientes a cada símbolo no terminal<br />

se añaden dos procedimientos nuevos: checkInput y scanTo.<br />

Esquematizado en seudocódigo quedaría de la siguiente forma:<br />

procedimiento scanTo(synchset);<br />

begin<br />

while not (token in synchset ∪ {$}) do<br />

getToken;<br />

end scanTO;<br />

procedimiento checkInput(FIRSTSet, FOLLOWSet);<br />

begin<br />

if not (token in FIRSTSet) then<br />

error;<br />

scanTo (FIRSTSet ∪ FOLLOWSet);<br />

end if;<br />

end checkInput;<br />

Es el consumidor de<br />

tokens en modo de<br />

alarma<br />

Realiza la<br />

verificación temprana<br />

en modo de alarma<br />

Página 67


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Recuperación de errores en ASD recursivos<br />

Estos procedimientos se utilizan como sigue en los procedimientos exp y factor:<br />

procedimiento exp(synchset);<br />

begin<br />

checkInput ({(,numero}, synchset);<br />

if not (token in synchset) then<br />

term (synchset);<br />

while token = + or token = - do<br />

match (token);<br />

term (synchset);<br />

end while;<br />

checkInput(synchset, {(,numero});<br />

end if;<br />

end exp;<br />

procedimiento factor(synchset);<br />

begin<br />

checkInput ({(,numero}, synchset);<br />

if not (token in synchset) then<br />

case token of<br />

( : match (();<br />

exp ({)});<br />

match ());<br />

numero:<br />

match(numero);<br />

else error;<br />

end case;<br />

checkInput(synchset, {(,numero});<br />

end if;<br />

end factor;<br />

Página 68


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Recuperación de errores en ASD recursivos<br />

checkInput es llamado dos veces en cada procedimiento:<br />

Para verificar que un token en el conjunto Iniciales sea el token<br />

siguiente en la entrada.<br />

Para verificar que un token en el conjunto Siguiente sea el token<br />

siguiente en la salida.<br />

En general synchset se pasa en las llamadas recursivas con nuevos tokens<br />

de <strong>sin</strong>cronización agregados de manera apropiada.<br />

Para obtener los mejores mensajes de error y recuperación de errores, toda<br />

prueba de token se debe examinar por la probabilidad de que una prueba más<br />

general o más temprana mejore el comportamiento del error.<br />

Página 69


Tratamiento de errores <strong><strong>sin</strong>táctico</strong>s<br />

Recuperación de errores<br />

Wirth especifica las características de un buen analizador <strong><strong>sin</strong>táctico</strong>:<br />

1. Ninguna sentencia debe dar lugar a que el analizador <strong><strong>sin</strong>táctico</strong> pierda el<br />

control.<br />

2. Todos los errores <strong><strong>sin</strong>táctico</strong>s deben de ser detectados y señalados.<br />

3. Los errores muy frecuentes e imputables a verdaderos fallos de comprensión<br />

o descuido del programador, habrán de ser diagnosticados correctamente<br />

(evitar los llamados mensajes de rebote). Esta tercera característica es la más<br />

difícil de lograr, ya que incluso compiladores de gran calidad emiten dos o más<br />

mensajes para un determinado error.<br />

Página 70

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!