análisis sintáctico descendente sin retroceso - Blearning
análisis sintáctico descendente sin retroceso - Blearning análisis sintáctico descendente sin retroceso - Blearning
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
- Page 2 and 3: Objetivos Conocer el análisis desc
- Page 4 and 5: Introducción Clasificación métod
- Page 6 and 7: Introducción A. Sintáctico Descen
- Page 8 and 9: El problema del retroceso Análisis
- Page 10 and 11: El problema del retroceso Análisis
- Page 12 and 13: Análisis sintáctico descendente c
- Page 14 and 15: Análisis sintáctico descendente s
- Page 16 and 17: Análisis sintáctico descendente s
- Page 18 and 19: Análisis sintáctico descendente s
- Page 20 and 21: Análisis sintáctico descendente s
- Page 22 and 23: Análisis sintáctico descendente s
- Page 24 and 25: Análisis sintáctico descendente s
- Page 26 and 27: Análisis sintáctico descendente s
- Page 28 and 29: Análisis sintáctico descendente s
- Page 30 and 31: Análisis sintáctico descendente s
- Page 32 and 33: Análisis sintáctico descendente s
- Page 34 and 35: Análisis sintáctico descendente s
- Page 36 and 37: Análisis sintáctico descendente s
- Page 38 and 39: Análisis sintáctico descendente s
- Page 40 and 41: Análisis sintáctico descendente s
- Page 42 and 43: Construcción de analizadores sint
- Page 44 and 45: Construcción de analizadores sint
- Page 46 and 47: Construcción de analizadores sint
- Page 48 and 49: Construcción de analizadores sint
- Page 50 and 51: Construcción de analizadores sint
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