análisis sintáctico descendente sin retroceso - Blearning

análisis sintáctico descendente sin retroceso - Blearning análisis sintáctico descendente sin retroceso - Blearning

blearning.itmina.edu.mx
from blearning.itmina.edu.mx More from this publisher
09.05.2013 Views

Universidad de Oviedo - Departamento de Informática Escuela Politécnica Superior de Ingeniería ANÁLISIS SINTÁCTICO EN PROCESADORES DE LENGUAJE TEMA 5 ANÁLISIS SINTÁCTICO DESCENDENTE Página 1

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!