1. Análisis Sintáctico
Tema 3
Juan A. Bot´a Blaya
ı
juanbot@um.es
http://ants.dif.um.es/staff/juanbot/traductores/traductores.html
´
Departamento de Ingenier´a de la Informacion y las Comunicaciones
ı
Universidad de Murcia
´ ´
Analisis Sintactico – p.1/64
2. Índice
Introducción al Análisis Sintáctico.
1. Objetivo del analizador sintáctico
2. Gramáticas libres de contexto y autómatas con pila
3. Análisis sintáctico ascendente y descendente
4. El problema de la ambigüedad en el análisis sintáctico
5. Métodos Universales de análisis sintáctico
(a) El método de Cocke-Younger-Kasami
(b) El método de Early
6. Transformaciones de gramáticas
´ ´
Analisis Sintactico – p.2/64
3. Gramáticas libres de contexto (CFG) y Autómatas de pila (PA)
Definición 1 Una gramática libre de contexto G = (V N , Vt , S, P ) es aquella cuyas
producciones tienen la forma A → α, siendo A ∈ VN y α ∈ (VN VT )∗ .
Definición 2 Un autómata de pila se define como una 7-tupla AP = (Q, V, Σ, δ, q 0 , z0 , F )
en donde:
Q es un conjunto finito de estados.
V es el alfabeto de entrada.
Σ es el alfabeto de la pila.
q0 es el estado inicial.
z0 es el símbolo inicial de la pila.
F ⊆ Q es el conjunto de estados finales.
δ es la función de transición:
∗
δ: Q × (V ∪ {λ}) × Σ → 2Q×Σ
´ ´
Analisis Sintactico – p.3/64
4. Gramáticas libres de contexto (CFG) y Autómatas de pila (PA)
Definición 3 Se entiende por configuración de un atomómata con pila a su situación en un
instante considerado expresada formalmente por medio de una tripla
(q, w, α) ∈ (Q × V ∗ × Σ∗ ) en donde:
q ∈ Q es el estado actual del autómata.
w ∈ V ∗ es la subcadena de entrada que aun no se ha analizado.
α ∈ Σ∗ es el contenido actual de la pila.
Si w = λ no queda nada por analizar. Si α = λ se ha reconocido la cadena.
´ ´
Analisis Sintactico – p.4/64
5. Gramáticas libres de contexto (CFG) y Autómatas de pila (PA) (II)
Definición 4 Un movimiento de un AP es una transición entre configuraciones. Una
transición no tiene porqué realizarse en un solo movimiento.
Por ej. el movimiento (q, aw, Zα) (q , w, βα) es un movimiento válido siempre y cuando
(q , β) ∈ δ(q, a, Z) con q ∈ Q, a ∈ (V ∪ λ), w ∈ V ∗ , α, β ∈ Σ∗ .
Se debe señalar que un AP no puede realizar ningún movimiento si la pila está vacía.
Entonces, un autómata de pila reconocerá una cadena de entrada sii partiendo de su
configuración inicial, (q0 , t, Z0 ), llega a una configuración final (qf , λ, α) empleando
movimientos válidos y lo expresamos:
∗
(q0 , t, Z0 ) (qf , λ, α), qf ∈ F, α ∈ Σ∗
La cadena será aceptada por vaciado de pila si despues de leerse toda la cadena se llega a
un estado con la pila vacía, independientemente del tipo de estado en el que se encuentre
el AP.
´ ´
Analisis Sintactico – p.5/64
6. Gramáticas libres de contexto (CFG) y Autómatas de pila (PA) (III)
Ejemplo. Sea G = (VN , VT , P, S) con P
S →S+A
S→A
A→A∗B
A→B
B → (S)
B→a
Sea AP = (Q, V, Σ, δ, q, s, ∅) en donde Q = {q}, V = {a, +, ∗, (, )} y δ:
δ(q, λ, S) = {(q, S + A), (q, A)}
δ(q, λ, A) = {(q, A ∗ B), (q, B)}
δ(q, λ, B) = {(q, (S)), (q, a)}
δ(q, op, op) = {(q, λ)}
siendo op ∈ V . Intentaremos reconocer la cadena w = a + a ∗ a.
´ ´
Analisis Sintactico – p.6/64
8. Análisis Sintáctico Ascendente y Descendente
Definición 5 Una sentencia w ∈ L(G), para alguna CFG (Context Free Grammar) ha sido
reconocida cuando conocemos alguno de (o quizá todos) sus árboles de derivación.
Definición 6 Sea una gramática G = (VN , VT , P, S). Se dice que la cadena α deriva
directamente a la cadena β, denotándolo α ⇒ β, si se puede escribir
α = δAµ y β = δγµ
para alguna cadena δ y µ ∈ (VT ∪ VN )∗ , y además existe A → γ ∈ P .
Definición 7 Sea una gramática G = (VN , VT , P, S). Para cualquier A ∈ VN y
∗
α ∈ (VN ∪ VT )∗ se dice que A =⇒ α si α se deriva de A, con una cadena de derivaciones
de longitud cualqiera, incluso nula.
´ ´
Analisis Sintactico – p.8/64
9. Análisis Sintáctico Ascendente y Descendente
Definición 8 Sea una gramática G = (VN , VT , P, S). Las formas sentenciales de G vienen
dadas por el conjunto
∗
D(G) = {α / S =⇒ α y α ∈ (VN ∪ VT )∗ }
Definición 9 El lenguaje definido por una gramática G, denotado L(G) es el conjunto de
cadenas de símbolos terminales, que se pueden derivar partiendo del axioma de la
gramática, y empleando para las derivaciones las reglas de P . E.d.:
∗
L(G) = {x/S =⇒ x, y x ∈ T ∗ }
Definición 10 Sea una gramática G = (VN , VT , P, S). Sea una forma sentencial αβγ en
donde α ∈ VT , β ∈ VN y γ ∈ (VT ∪ VN )∗ . Una derivación izquierda se obtiene
∗
sustituyendo β por alguna de las partes derechas que la definen.
Definición 11 Sea una gramática G = (VN , VT , P, S). Sea una forma sentencial αβγ en
donde α ∈ (VT ∪ VN )∗ , β ∈ VN y γ ∈ VT . Una derivación derecha se obtiene sustituyendo
∗
β por alguna de las partes derechas que la definen.
´ ´
Analisis Sintactico – p.9/64
10. Análisis Sintáctico Ascendente y Descendente (II)
A → BF
B → EC
Un ejemplo de este tipo de derivaciones, para la gramática E → a puede verse
C → b
F → c
A
D Derivación derecha
I Derivación izquierda
BF
I D
ECF Bc
I
D
aCF EbF ECc ECc
I D
abF aCc abF Ebc aCc Ebc aCc Ebc
I D
en el árbol abc abc abc abc abc abc abc abc
´ ´
Analisis Sintactico – p.10/64
11. Análisis Sintáctico Ascendente y Descendente (III)
Existen dos grandes grupos de métodos de análisis sintáctico, dependiendo de la dirección
en la que se recorre el árbol sintáctico.
Descendente: en este tipo de análisis, se va recorriendo el árbol sintáctico desde la raíz
hasta las hojas, llegando a generar la sentencia que se está analizando. La raíz
representa al símbolo inicial de la gramática.
Ascendente: se parte de las hojas y se intenta construir el árbol hacia arriba, hasta
llegar al símbolo inicial de la gramática.
En un análisis top-down un parser hacer corresponder cadenas de entrada con sus
correspondientes derivaciones izquierdas.
En un análisis bottom-up un parser hace corresponder cadenas de entrada con las inversas
de las correspondientes derivaciones derechas.
Entre los métodos generales, los algoritmos de Cocke-Younger-Kasami (CYK) y el método de
Early son los más conocidos → son bastante ineficientes desde un punto de vista
computacional.
Para la mayoría de lenguajes de programación es suficiente con trabajar con subconjuntos
de las CFG, como los de las LL y LR que permiten algoritmos de parsing más eficientes.
´ ´
Analisis Sintactico – p.11/64
12. El problema de la ambigüedad en el análisis sintáctico
Definición 12 Un árbol ordenado y etiquetado D es un árbol de derivación para una
gramática libre de contexto G(S) = (VN , VT , P, S) si:
1. La raíz de D está etiquetada con S.
2. Si D1 , . . . , Dk son los subárboles de los descendientes directos de la raíz, y la raíz de
cada Di está etiquetada con Xi , entonces S → X1 · · · Xk ∈ P . Además Di debe ser
un árbol de derivación en G(Xi ) = (VN , VT , P, Xi ) si Xi ∈ VN , bien un nodo hoja
con etiqueta Xi si Xi ∈ VT .
3. Alternativamente, si D1 es el único subárbol de la raíz de D, y la raíz de D 1 tiene
como etiqueta e, entonces S → e ∈ P .
Definición 13 La frontera de un árbol de derivación es la cadena que se obtiene
concatenando, de izquierda a derecha, las etiquetas de las hojas.
Definición 14 Sea una CFG G = (VN , VT , P, S). Decimos que G es ambigua si existe al
menos una sentencia w ∈ L(G) para la cual hay un árbol de derivación distinto, con frontera
w.
´ ´
Analisis Sintactico – p.12/64
13. El problema de la ambigüedad en el análisis sintáctico (II)
El hecho de que una gramática sea ambigua es una situación indeseable ya que cada árbol
de derivación representa una ejecución distinta de la misma sentencia, y por lo tanto cada
uno podría producir resultados distintos.
sea la gramática G = (VN , VT , P, S) con P = {E → E + E, E → E ∗ E, E → a}
Ejemplo:
Caben dos interpretaciones para a + a ∗ a:
E E
E + E E * E
a E * E E + E a
a a a a
´ ´
Analisis Sintactico – p.13/64
14. El problema de la ambigüedad en el análisis sintáctico (III)
Otro ejemplo clásico de este tipo de problemas es el de las gramáticas que incluyen
sentencias del tipo if-then/if-then-else.
Sea la gramática
prop → if expr then prop
| if expr then prop else prop
| otra
De acuerdo con ella, la sentencia
if E1 then S1 else if E2 then S2 else S3
no es ambigua, ya que el árbol de derivación correspondiente sería
prop
if expr then prop else prop
E1 S1
if expr then prop else prop
E2 S2 S3
´ ´
Analisis Sintactico – p.14/64
15. El problema de la ambigüedad en el análisis sintáctico (IV)
sin embargo, la sentencia
if E1 then if E1 then S1 else S2
si lo sería, ya que daría lugar a la siguiente pareja de árboles de
derivación distintos.
prop
prop
if expr then prop
E1 if expr then prop else prop
E1 S1
if expr then prop else prop
E2 S1 S2 if expr then prop
E2 S2
´ ´
Analisis Sintactico – p.15/64
16. El problema de la ambigüedad en el análisis sintáctico (V)
Hay dos enfoques distintos usados para solucionar este
problema.
1. Transformar la definición del lenguaje para que las
construcciones if-then-else tengan delimitadores de
bloque, y los else se asocien con los if explícitamente.
2. Transformar la gramática en otra equivalente y que no
sea ambigua.
´ ´
Analisis Sintactico – p.16/64
17. El problema de la ambigüedad en el análisis sintáctico (VI)
Ejemplo con el método 1:
prop → if expr then prop endif
| if expr then prop else prop endif
| otra
entonces, para escribir una sentencia como la del ejemplo, y en la que se asocie el else al
segundo if quedaría
if E1 then if E1 then S1 else S2 endif endif
Una sentencia que ahora asociara el else con el primer if sería
if E1 then if E1 then S1 endif else S2 endif
´ ´
Analisis Sintactico – p.17/64
18. El problema de la ambigüedad en el análisis sintáctico (VII)
Ejemplo con el método 2:
Se debe elegir entre los dos árboles de la transparencia 10.
Elegir el árbol de la izquierda implica emparejar el else con el then anterior y sin emparejar
más cercano.
Elegir el árbol de la derecha implica emparejar el else con el then más lejano y que aun esté
sin emparejar.
Eligiendo el árbol de la izquierda, se dividen las proposiciones entre emparejadas y no
emparejadas.
Toda proposición que aparezca entre un then y un else debe estar emparejada, e.d. no debe
terminar con un then sin emparejar porque entonces el else estaría obligado a concordar con
ella.
Una proposición emparejada es ó una proposición if-then-else que no contenga proposiciones
sin emparejar o cualquier otra clase de proposición no condicional.
prop → prop_emparejada
| prop_no_emparejada
prop_emparejada → if expr then prop_emparejada else prop_emparejada
| otra
prop_no_emparejada → if expr then prop
| if expr then prop_emparejada else prop_no_emparejada
´ ´
Analisis Sintactico – p.18/64
19. Métodos universales de análisis sintáctico
Aplicables a cualquier GLC
Métodos tabulares
Complejidad espacial O(n2 ) y temporal O(n3 )
No aplicables para lenguajes de programación
convencionales
Aplicables si interesan todos los árboles de derivación
posibles (G. ambiguas)
´ ´
Analisis Sintactico – p.19/64
20. Algoritmo Cocke-Younger-Kasami
Basado en programación dinámica
Poca aplicabilidad
Complejidad espacial proporcional a n2 (n longitud de
w)
Complejidad temporal proporcional a n3
Algoritmo de Early consigue complejidades lineales
para muchas gramáticas LC
Necesita gramáticas en CNF y λ−libres
´ ´
Analisis Sintactico – p.20/64
21. CYK-Funcionamiento básico
Sea w = a1 a2 . . . an con ai ∈ VT ∀i = 1, . . . , n
El algoritmo construye una tabla T triangular, con elementos
tij ⊂ VN , 1 ≤ i ≤ n y 1 ≤ j ≤ n − i + 1,
j
t11 t12 t13 t14
t21 t22 t23
i
t31 t32
t41
en donde A ∈ tij sii A ⇒+ ai ai+1 · · · ai+j−1
Entonces w ∈ L(G) si S ∈ t1n
´ ´
Analisis Sintactico – p.21/64
22. Métodos Universales de análisis sintáctico. El algoritmo CYK
Algoritmo 1 Algoritmo de análisis sintáctico de Cocke-Younger-Kasami.
Entrada: Una gramática G = (VN , VT , P, S) en CNF y sin λ-producciones, junto con una
∗
cadena de entrada w = a1 a2 · · · an ∈ VT .
+
Salida: La tabla T en la que cada ti,j contiene a A ∈ VN sii A =⇒ ai ai+1 · · · ai+j−1 .
Método:
1. Hacer ti,1 = {A|A → ai ∈ P } para todo i.
2. Supongamos que ti,j se ha calculado para todo i, 1 ≤ i ≤ n, y para todo j ,
1 ≤ j < j. Hágase
ti,j = {A| para algun k , 1 ≤ k < j, A → BC ∈ P, B ∈ ti,k , y C ∈ ti+k,j−k }
Dado que i ≤ k < j, tanto k como j − k son menores que j. Por lo tanto, t i,k y
ti+k,j−k han sido calculados antes de ti,j . Después de este paso, si ti,j contiene a
A, entonces
+ +
A ⇒ BC =⇒ ai · · · ai+k−1 C =⇒ ai · · · ai+k−1 ai+k · · · ai+j−1
3. Realizar el paso anterior, hasta que ti,j haya quedado calculado para todo 1 ≤ i ≤ n
y 1 ≤ j ≤ n − i + 1.
´ ´
Analisis Sintactico – p.22/64
23. CYK-Observaciones
Se trata de encontrar A, B, C tales que
C
A B
a1 a2 . . . ai . . .. . . ai+j−1 . . . an
hasta que al final tengamos
S
D E
a1 a2 . . .. . . an
y por lo tanto podamos asegurar que
S → DE ⇒+ a1 a2 . . . E ⇒+ a1 a2 . . . an
´ ´
Analisis Sintactico – p.23/64
24. Métodos Universales de análisis sintáctico. El algoritmo CYK (II)
Veámos la aplicación del algoritmo con un ejemplo en el que el conjunto P de nuestra
gramática viene dado por el conjunto de producciones
S → AA|AS|b
A → SA|AS|a
Sea w = abaab la cadena de entrada. Aplicando el algoritmo, la tabla resultante será una
triangular de 5 × 5. Aplicando el paso 1, tenemos
t11 = {A}, ya que A → a ∈ P .
t21 = {S}, ya que S → b ∈ P .
t31 = {A}, ya que A → a ∈ P .
t41 = {A}, ya que S → a ∈ P .
t51 = {S}, ya que A → b ∈ P .
´ ´
Analisis Sintactico – p.24/64
25. Métodos Universales de análisis sintáctico. El algoritmo CYK (III)
Ahora, hacemos j = 2. Tenemos que ti,j se ha calculado para j = 1. Tenemos que
encontrar no-terminales que produzcan subcadenas de w de longitud 2.
t1,2 = {S, A}, ya que 1 ≤ k < 2, y la regla ha de ser tal que el primer no-terminal de
la parte derecha esté en t1,1 y el segundo no-terminal en t2,1 . Por lo tanto la parte
derecha ha de ser AS. Tanto S como A tienen reglas con esa parte derecha.
t22 = {A}, ya que 1 ≤ k < 2, y la regla ha de ser tal que el primer no-terminal de la
parte derecha esté en t2,1 y el segundo no-terminal en t3,1 . La parte derecha sería
SA. Únicamente A tiene partes derechas de ese tipo.
t3,2 = {S}, ya que 1 ≤ k < 2, y la regla ha de ser tal que el primer no-terminal de la
parte derecha esté en t3,1 , y el segundo en t4,1 . Por lo tanto, la parte derecha sería
AA. Sólamente S tiene reglas de producción con esa parte derecha.
t4,2 = {A, S}, ya que 1 ≤ k < 2, y la regla ha de ser tal que el primer no-terminal de
la parte derecha esté en t4,1 , y el segundo en t5,1 . Por lo tanto, la parte derecha sería
AS.
´ ´
Analisis Sintactico – p.25/64
26. Métodos Universales de análisis sintáctico. El algoritmo CYK (IV)
Después de hacer el paso 2, con j = 2 la tabla T queda así:
1 2 3 4 5
1 {A} {S, A}
2 {S} {A}
3 {A} {S}
4 {A} {A, S}
5 {S}
´ ´
Analisis Sintactico – p.26/64
27. Métodos Universales de análisis sintáctico. El algoritmo CYK (V)
Ahora, hacemos j = 3. Tenemos que ti,j se ha calculado para 1 ≤ j < 3. Tenemos que
encontrar no-terminales que produzcan subcadenas de w, de longitud 3.
t1,3 = {A, S}, ya que 1 ≤ k < 3, y la regla ha de ser tal que el primer no-terminal de
la parte derecha esté en t1,1 (o en t1,2 ) y el segundo no-terminal en t2,2 (o en t3,1 ).
Por lo tanto la parte derecha ha de ser AA (o SA).
t2,3 = {S}, ya que 1 ≤ k < 3, y la regla ha de ser tal que el primer no-terminal de la
parte derecha esté en t2,1 (o en t2,2 ) y el segundo no-terminal en t3,2 (o en t4,1 ). La
parte derecha sería SS (o AA). Únicamente S tiene una producción S → AA.
t3,3 = {A, S}, ya que 1 ≤ k < 3, y la regla ha de ser tal que el primer no-terminal de
la parte derecha esté en t3,1 (o en t3,2 ), y el segundo en t4,2 (o en t5,1 ). Con t3,1 y
t4,2 tenemos dos posibles partes derechas que son AA y AS, y por ello tanto S como
A deben estar en t3,3 . Con t3,2 y t5,1 tenemos como parte derecha SS, que no es
generada por ningun no-terminal.
´ ´
Analisis Sintactico – p.27/64
28. Métodos Universales de análisis sintáctico. El algoritmo CYK (VI)
Después de hacer el paso 2, con j = 3 la tabla T queda así:
1 2 3 4 5
1 {A} {S, A} {A, S}
2 {S} {A} {S}
3 {A} {S} {A, S}
4 {A} {A, S}
5 {S}
´ ´
Analisis Sintactico – p.28/64
29. Métodos Universales de análisis sintáctico. El algoritmo CYK (VII)
Ahora, hacemos j = 4. Tenemos que ti,j se ha calculado para 1 ≤ j < 4. Tenemos que
encontrar no-terminales que produzcan subcadenas de w, de longitud 4.
t1,4 = {A, S}, ya que 1 ≤ k < 4, y la regla ha de ser tal que el primer no-terminal de
la parte derecha esté en t1,1 , k = 1, o en t1,2 , k = 2, o en t1,3 , k = 3 y el segundo
no-terminal en t2,3 , k = 1, o en t3,2 , k = 2 o en t4,1 . El conjunto de no-terminales que
podrían formar el primer no-terminal de la parte derecha es {A, S} y el de
no-terminales que podrían formar el segundo no-terminal de la parte derecha es
{A, S}. Por lo tanto la parte derecha va a estar en {AA, AS, SA, SS}.
t2,4 = {A, S}, ya que 1 ≤ k < 4, y la regla ha de ser tal que el primer no-terminal de
la parte derecha esté en t2,1 , k = 1, o en t2,2 , k = 2 o en t2,3 , k = 3 y el segundo
no-terminal en t3,3 , k = 1, o en t4,2 , k = 2, o en t5,1 . El conjunto de no-terminales
que podrían formar el primer no-terminal de la parte derecha es {A, S}, y el de
no-terminales que podrían formar el segundo no-terminal de la parte derecha es
{A, S}. Por lo tanto la parte derecha va a estar en {AA, AS, SA, SS}.
´ ´
Analisis Sintactico – p.29/64
30. Métodos Universales de análisis sintáctico. El algoritmo CYK (VIII)
Después de hacer el paso 2, con j = 4 la tabla T queda así:
1 2 3 4 5
1 {A} {S, A} {A, S} {A,S}
2 {S} {A} {S} {A, S}
3 {A} {S} {A, S}
4 {A} {A, S}
5 {S}
Hacer el paso 2, con j = 5 en clase, y completar la tabla T
con t1,5 .
´ ´
Analisis Sintactico – p.30/64
31. Obteniendo una secuencia de derivaciones a partir de T
Especifiación algorítmica
Algoritmo 2 Derivación más a la izquierda a partir de la tabla T de parsing.
Entrada: una gramática G = (VN , VT , P, S) en formato CNF, y en la que las
producciones de P están numeradas de 1 a p, una cadena de entrada
w = a1 a2 · · · an , y la tabla T generada por el algoritmo CYK.
Salida: una derivación izquierda de w o un error.
Método: se va a basar en el uso de una rutina recursiva gen(i, j, A) que va a generar
+
la derivación A =⇒ ai ai+1 · · · ai+j−1 . Se define como sigue:
1. Si j = 1 y la producción m-ésima es A → ai , entonces la salida de gen(i, 1, A)
es m.
2. Si j > 1, sea k el entero más pequeño, 1 ≤ k < j, tal que para algún B ∈ t i,k y
C ∈ ti+k,j−k se tiene que A → BC ∈ P . Si hay varias, elegimos la que tenga el
índice más pequeño, digamos m. La salida de gen(i, j, A) es m, más las salidas
de gen(i, k, B) y gen(i + k, j − k, C).
Por lo tanto, para obtener la derivación para w llamamos a gen(1, n, S)
´ ´
Analisis Sintactico – p.31/64
32. Obteniendo una secuencia de derivaciones a partir de T
Tomemos la gramática del ejemplo anterior y dispongámosla en el orden
siguiente:
(1)S → AA
(2)S → AS
(3)S → b
(4)A → SA
(5)A → AS
(6)A → a
Sea la cadena de entrada w = abaab la misma que para T
Tenemos que llamar a gen(1, 5, S), siempre que S ∈ t1,5
´ ´
Analisis Sintactico – p.32/64
33. Obteniendo una secuencia de derivaciones a partir de T
Ahora la evolución, con el k y m correspondientes es
k=1
gen(1,5,S) m=1
k=1 k=1
gen(1,1,A) m=6 gen(2,4,A) m=4
k=1 k=1
gen(2,1,S) m=3 gen(3,3,A) m=5
k=1 k=1
gen(3,1,A) m=6 gen(4,2,5)m=2
k=1 k=1
gen(4,1,A) m=6 gen(5,1,S)m=3
En esa figura puede verse que la secuencia de derivaciones obtenida es la siguiente
S ⇒1 AA ⇒6 aA ⇒4 aSA ⇒3 abA ⇒5 abAS ⇒6 abaS ⇒2 abaAS ⇒6 abaaS ⇒3 abaab
´ ´
Analisis Sintactico – p.33/64
34. El algoritmo de Early
Complejidad proporcional a n2 si gramática no es ambigua
Para lenguajes más usados, complejidades espacial y temporal son
lineales
Partimos de
La gramática G = (VN , VT , P, S), de tipo CFG.
∗
Una cadena w = a1 a2 · · · an , en donde w ∈ VT .
Un elemento [A → X1 X2 · · · Xk • Xk+1 · · · Xm , i] es lo que vamos a
denominar un item para la cadena w, si A → X1 · · · Xm ∈ P y
0 ≤ i ≤ n.
El punto • es un símbolo adicional, y el entero k es tal que
0 ≤ k ≤ m.
Si la producción es A → λ entonces el item es [A → •, i]
´ ´
Analisis Sintactico – p.34/64
35. El algoritmo de Early (II)
El algoritmo trabaja construyendo un conjunto de items I j para cada j, 0 ≤ j ≤ n, de
tal forma que [A → α • β, i] ∈ Ij , con 0 ≤ i ≤ j sii para algún γ y δ, se tiene que:
∗
S ⇒ γAδ
∗
γ ⇒ a1 · · · a i
∗
α ⇒ ai+1 · · · aj
Los índices i y j delimitan el segmento de cadena en w que se produce por la cadena
que está a la izquierda del símbolo •, si se observa la tercera derivación.
Las dos derivaciones primeras aseguran que el prefijo izquierdo de la cadena, desde
a1 hasta ai se ha producido a partir del símbolo inicial de la gramática.
La secuencia de listas de items generada, I 0 , I1 , . . . , In se denomina listas del parser,
para la cadena de entrada w.
w ∈ L(G) sii existe algún item en la forma [S → α•, 0] ∈ In .
´ ´
Analisis Sintactico – p.35/64
36. El algoritmo de Early (III)
Algoritmo 3 El algoritmo de parsing de Early
Entrada: la gramática CFG, G = (VN , VT , P, S) y la cadena de entrada
∗
w = a 1 a2 · · · a n ∈ V T .
Salida: Las listas del parser, I0 , I1 , . . . , In .
Método: Primero se construye I0 usando los pasos (1) a (3)
1. Si S → α ∈ P entonces, añadir el item [S → •α, 0] a I0 .
Ejecutar (2) y (3) hasta que no se añada ningún item nuevo a I 0 .
2. Si [B → γ•, 0] ∈ I0 , añadir [A → αB • β, 0] para todo [A → α • Bβ, 0] ∈ I0 .
3. Sea [A → α • Bβ, 0] ∈ I0 . Para toda producción B → γ, añadir el item [B → •γ, 0] a
I0 , siempre que no estuviera añadido ya.
Ahora, sea I0 , I1 , . . . , Ij−1 el conjunto de listas de items ya construidos. Para la
construcción de Ij , hacer:
4. Para cada [B → α • aβ, i] ∈ Ij−1 tal que a = aj , añadir [B → αa • β, i] a Ij .
Ejecutar los dos últimos pasos, para cada j, hasta que no se pueda añadir ningún
item más a Ij .
5. Sea [A → γ•, i] ∈ Ij . Si existe un [B → α • Aβ, k] ∈ Ii , entonces, añadir
[B → αA • β, k] a Ij .
6. Sea [A → α • Bβ, i] ∈ Ij . Para todo B → γ ∈ P , añadimos [B → •γ, j] a Ij .
´ ´
Analisis Sintactico – p.36/64
37. Interpretación del algoritmo
Paso 1: Iniciamos la construcción de la tabla con todas las producciones de S
Paso 2: Exploramos todas las λ−producciones a partir de S
Paso 3: Si en I0 tenemos A → B, debemos incluir también todas las producciones de
B
Paso 4: Si [B → α • aβ, i] ∈ Ij−1 sabemos entonces que (suponiendo que w ∈ L(G))
α
a1 a2 . . . ai+1 ai+2 . . . aj−1 aj . . . an
entonces, si a = aj podemos decir que
αa≡αaj
a1 a2 . . . ai+1 ai+2 . . . aj−1 aj . . . an
por lo tanto incluimos [B → αa • β, i] → Ij
´ ´
Analisis Sintactico – p.37/64
38. Interpretación del algoritmo
Paso 5: Si [A → γ•, i] ∈ Ij tenemos que
γ
. . . ai+1 ai+2 . . . aj . . .
y como A → γ entonces decimos también que
A
γ
ai+1 ai+2 . . . aj
entonces, si tenemos que [B → α • Aβ, k] ∈ Ii significa que tenemos
A
α γ
ak+1 . . . ai ai+1 ai+2 . . . aj
por lo que hacemos [B → αA • β, k] → Ij
Paso 6: análogo al 3
´ ´
Analisis Sintactico – p.38/64
39. Ejemplo
Veámos la aplicación del algoritmo con un ejemplo: sea G = (V N , VT , P, E) en donde P
viene dado por
1. E → T + E
2. E → T
3. T → F ∗ T
4. T → F
5. F → (E)
6. F → a
Y sea w = (a + a) ∗ a la cadena de entrada.
Según el paso (1) añadimos a I0 los items [E → •T + E, 0] y [E → •T, 0].
Dado que la gramática es λ−libre, el paso (2) no incorpora ningún item adicional.
Si en el paso (3) hacemos α = λ tenemos que incluir [T → •F ∗ T, 0] y [T → •F, 0].
En otra iteración más del paso (3) incluimos además [F → •(E), 0], y [F → •a, 0].
´ ´
Analisis Sintactico – p.39/64
40. Ejemplo
No podemos incluir más. El contenido de I0 queda:
[E → •T + E, 0]
[E → •T, 0]
[T → •F ∗ T, 0]
[T → •F, 0]
[F → •(E), 0]
[F → •a, 0]
Así que pasamos a construir ahora I1 . Por el paso (4)
observamos que [F → •(E), 0] cumple que (= a1 , y por lo
tanto añadimos [F → (•E), 0] a I1 .
´ ´
Analisis Sintactico – p.40/64
41. Ejemplo
Por el paso (6), intentamos desplazar el metasímbolo a la derecha
del no-terminal E, y para ello añadimos [E → •T + E, 1] y
[E → •T, 1] a I1 . A su vez, estos generan la adición de
[T → •F ∗ T, 1] y [T → •F, 1]. Estos generan también la
incorporación de [F → •(E), 1], y [F → •a, 1]. I1 queda entonces con
el siguiente contenido:
[F → (•E), 0]
[E → •T + E, 1]
[E → •T, 1]
[T → •F ∗ T, 1]
[T → •F, 1]
[F → •(E), 1]
[F → •a, 1]
Construímos ahora I2 . Ahora a2 = a. Por el paso (4), añadimos
[F → a•, 1].
´ ´
Analisis Sintactico – p.41/64
42. Ejemplo
Ahora, por el paso (5) intentamos aprovechar el hecho de que se ha
reconocido parcialmente a para reducir el no-terminal que la
produce, y por ello introducimos [T → F • ∗T, 1] y [T → F •, 1].
Dado que acabamos de añadir [T → F •, 1], nuevamente, por el paso
(5) añadimos [E → T • +E, 1] y [E → T •, 1]. Este último causa la
introducción de [F → (E•), 0]. Ahora, I2 está completo.
El contenido de I2 queda:
[F → a•, 1]
[T → F • ∗T, 1]
[T → F •, 1]
[E → T • +E, 1]
[E → T •, 1]
[F → (E•), 0]
´ ´
Analisis Sintactico – p.42/64
43. Ejemplo
La colección de cjtos. de items queda:
I1 I2 I3 I4
[F → (•E), 0] [E → T + •E, 1] [F → a•, 3]
[F → a•, 1] [T → F • ∗T, 3]
[E → •T + E, 1] [E → •T + E, 3]
[T → F • ∗T, 1] [T → F •, 3]
[E → •T, 1] [E → •T, 3]
[T → F •, 1] [E → T • +E, 3]
[T → •F ∗ T, 1] [T → •F ∗ T, 3]
[E → T • +E, 1] [E → T •, 3]
[T → •F, 1] [T → •F, 3]
[E → T •, 1] [E → T + E•, 1]
[F → •(E), 1] [F → •(E), 3]
[F → (E•), 0] [F → (E•), 0]
[F → •a, 1] [F → •a, 3]
I5 I6 I7
[F → a•, 6]
[F → (E)•, 0] [T → F ∗ •T, 0] [T → F • ∗T, 6]
[T → F • ∗T, 0] [T → •F ∗ T, 6] [T → F •, 6]
[T → F •, 0] [T → F •, 6] [T → F ∗ T •, 0]
[E → T • +E, 0] [F → •(E), 6] [E → T • +E, 0]
[E → T •, 0] [F → •a, 6] [E → T •, 0]
y como [E → T •, 0] ∈ I6 , tenemos que w ∈ L(G).
´ ´
Analisis Sintactico – p.43/64
44. Obtención de una derivación derecha
Algoritmo 4 Construcción de un árbol de derivación a partir de I 0 , I1 , . . . , In .
Entrada: una gramática CFG, libre de ciclos, G = (VN , VT , P, S), en donde las
producciones están numeradas, de 1 . . . p, una cadena w = a 1 . . . an , y la lista
I0 , I 1 , . . . , I n .
Salida: Ψ, un árbol de derivación derecho para w, ó un mensaje de error.
Método: si ∃ un [S → α•, 0] en In , entonces w ∈ L(G). Emitir salida de error. Si
/
no, hacer Ψ = λ. Ejecutar R([S → α•, 0], n), en donde R se define como sigue:
Rutina R([A → β•, i], j):
1. Sea h el índice de A → β. Entonces, hacer Ψ ← Ψ + h
2. Si β = X1 X2 · · · Xm , hacer k = m, l = j.
3. Mientras que k > 0 hacer:
(a) Si Xk ∈ VT , hacer k = k − 1 y l = l − 1.
(b) Si Xk ∈ VN ,
i. Encontrar un item [Xk → γ•, r] ∈ Il , para algún r
ii. Ejecutar R([Xk → γ•, r], l). Hacer k = k − 1 y l = r.
´ ´
Analisis Sintactico – p.44/64
45. Ejemplo
Vamos a obtener un árbol de derivación, que defina una derivación más a la derecha
para
1. E →T +E
2. E→T
3. T →F ∗T
4. T →F
5. F → (E)
6. F →a
Y w = (a + a) ∗ a la misma cadena de entrada que el ejemplo anterior. Usaremos
también la lista I1 , . . . , I7 producida en el ejemplo anterior.
Inicialmente ejecutamos R([E → T •, 0], 7). Ψ = {2}.
Paso 2. Ahora, β = T . Hacemos k = 1 y l = 7.
Paso 3b. Como X1 = T ∈ VN , tenemos que encontrar un item [T → γ•, r] ∈ I7 tal
que [E → •T, 0] esté en Ir .
El item [T → F •, 6] ∈ I7 no vale pues [E → •T, 0] ∈ I6 .
/
El item [T → F ∗ T •, 0] ∈ I7 si pues [E → •T, 0] ∈ I0 .
´ ´
Analisis Sintactico – p.45/64
46. Ejemplo
Llamamos a R([T → F ∗ T •, 0], 7):
Paso 2: β = F ∗ T , k = 3 y l = 7.
Paso 3b: Se ha de encontrar un [T → γ•, r] ∈ I7 tal que [T → F ∗ •T, 0] ∈ Ir .
Una opción no válida es [T → F ∗ T •, 0] porque [T → F ∗ •T.0] ∈ I 0 .
/
La opción correcta es [T → F •, 6] ya que [T → F ∗ •T, 0] ∈ I 6 .
Llamamos a R([T → F •, 6], 7).
Paso 2: β = F , k = 1 y l = 7.
Paso 3b: Se ha de encontrar un [F → γ•, r] ∈ I7 tal que [T → •F, 6] ∈ Ir . La
única opción es [F → a•, 6] porque [T → •F, 6] ∈ I6 .
Al llamar a R([F → a•, 6], 7) termina la ejecución de esta rama.
Paso 3bcont: k = k − 1. Termina.
´ ´
Analisis Sintactico – p.46/64
47. Ejemplo
Seguimos:
Seguimos con R([T → F ∗ T •, 0], 7)
Paso 3bcont: k = k − 1, l = l − 1.
Paso 3a: k = k − 1, k = 1, l = l − 1, l = 5.
Paso 3b: Se ha de encontrar un [F → γ•, r] ∈ I5 tal que [T → •F ∗ T, 0] ∈ Ir .
La única opción es [F → (E)•, 0] y [T → •F ∗ T, 0] ∈ I0 .
Llamamos a R([F → (E)•, 0], 5).
Paso 2: β = (E), k = 3 y l = 5.
Paso 3a: k = k − 1, k = 2, l = l − 1, l = 4.
´ ´
Analisis Sintactico – p.47/64
48. Ejemplo
Paso 3b: Se ha de encontrar un [E → γ•, r] ∈ I4 tal que [F → (•E), 0] ∈ Ir .
Tenemos las opciones:
[E → T • +E, 3] y [E → T •, 3], no válidas.
[E → T + E•, 1] válida ya que [F → (•E), 0] ∈ I1 .
Llamamos a R([E → T + E•, 1], 4):
Paso 2: β = T + E, k = 3 y l = 4
Paso 3b: Se debe encontrar un [E → γ•, r] ∈ I4 tal que [E → T + •E, 1] ∈ Ir .
1. [E → T + E•, 1] no es válida
2. [E → •, 3] si ya que, [E → T + •E, 1] ∈ I3 .
Llamamos a R([E → T •, 3], 4)
···
Se obtiene el árbol de derivación derecho 64642156432.
´ ´
Analisis Sintactico – p.48/64
49. Árbol de derivación obtenido
E R([E → T •, 0], 7)
T R([T → F ∗ T •, 0], 7)
R([F → (E)•, 0], 5) F * T R([T → F •, 6], 7)
( )
F R([F → a•, 6], 7)
E R([E → T + E•, 1], 4)
R([T → F •, 1], 2) T E a
R([E → T •, 3], 4)
+
R([F → a•, 1], 2) F T R([T → F •, 3], 4)
a F R([F → a•, 3], 4)
a
´ ´
Analisis Sintactico – p.49/64
50. Transformaciones de gramáticas
1. Eliminación de símbolos inútiles
2. Gramática λ-libre
3. Eliminación de producciones unitarias
4. Eliminación de la recursividad por la izquierda
5. Factorización
6. Forma normal de Chomsky
´ ´
Analisis Sintactico – p.50/64
51. Eliminación símbolos inútiles
Pasos para eliminar los símbolos no útiles de una gramática
1. Eliminación de variables (A ∈ VN ) improductivas.
2. Eliminación de símbolos inaccesibles.
Definición 15 Una variable A ∈ VN es improductiva si no existe ninguna
derivación tal que A ⇒∗ w con w ∈ VT .
∗
Definición 16 Un símbolo X es inaccesible si no aparece en ninguna
forma sentencial de la gramática, es decir, ¬∃α, β ∈ (VN VT )∗ tal que
S ⇒∗ αXβ.
Teorema 1 Dada una g.l.c. G = (VN , VT , S, P ), con L(G) = ∅, existe una
g.l.c. equivalente G = (VN , VT , S, P ) tal que ∀A ∈ VN se cumple que
existe una serie de derivaciones tal que A ⇒∗ w, w ∈ VT , es decir, existe
∗
una gramática equivalente sin variables improductivas.
´ ´
Analisis Sintactico – p.51/64
52. Eliminación de variables improductivas
El algoritmo para el cálculo de G (VN y P ) es el siguiente:
Algoritmo 5
begin
OLDV := ∅
∗
NEWV := {A ∈ VN |A → w ∈ P , w ∈ VT }
while OLDV = NEWV do
begin
OLDV := NEWV
NEWV := OLDV {A ∈ VN |A → α, α ∈ (VT OLDV )∗ }
end
VN := NEWV
P = {A → α ∈ P |A ∈ VN , α ∈ (VN VT )∗ }
end
´ ´
Analisis Sintactico – p.52/64
53. Eliminación de símbolos inaccesibles
Teorema 2 Dada una g.l.c. G = (VN , VT , S, P ), con L(G) = ∅, existe una
g.l.c. equivalente G = (VN , VT , S, P ) sin símbolos inaccesibles.
El algoritmo para el cálculo de G (VN , VT y P ) es el siguiente:
Algoritmo 6
begin
VN := {S}; VT := ∅; P := ∅;
repeat
for A ∈ VN , A → α1 |α2 | · · · |αn , no procesada a´n
u
{a˜adir todas las variables de αi a VN
n
a˜adir todos los terminales de αi a VT }
n
until VN no var´e ı
P = {A → α ∈ P |A ∈ VN ∧ α ∈ (VN VT )∗ }
end
´ ´
Analisis Sintactico – p.53/64
54. Eliminación de símbolos inútiles
Teorema 3 Dada una gramática libre de contexto G, con
L(G)= ∅, existe una GLC G’ equivalente sin símbolos inútiles.
Los pasos a seguir serían (el orden es importante):
Pasamos de G a G1 según el algoritmo 5
Pasamos de G1 a G’ según el algoritmo 6
G’ no contiene símbolo inútiles, es decir, todo símbolo
X ∈ (VN ∪ VT ) es tal que S ⇒∗ αXβ ⇒∗ w.
´ ´
Analisis Sintactico – p.54/64
55. Grámatica λ-libre
Definición 17 Decimos que una gramática l.c.
G = (VN , VT , S, P ) es λ-libre si cumple que en sus reglas de
producción no aparece ninguna de la forma A → λ, excepto a
los sumo S → λ, con la condición de que S no aparezca en la
parte derecha de ninguna otra regla de producción.
Teorema 4 Dada una g.l.c. G = (VN , VT , S, P ), existe una
g.l.c. equivalente G = (VN , VT , S , P ) que es λ-libre.
´ ´
Analisis Sintactico – p.55/64
56. Grámatica λ-libre
Algoritmo 7
1. Obtenemos Vλ = {A ∈ VN |A ⇒∗ λ}: Conjunto de variables anulables
Inicialmente Vλ contiene A si A → λ. Luego, si tenemos B → x1 x2 . . . xn y xi ∈ Vλ
∀i, añadir B.
2. Obtenemos P del siguiente modo:
Por cada producción A → x1 x2 . . . xk (k > 0) añadimos:
A → Y1 Y2 . . . Yn , dónde cada Yi es:
(a) Si xi no es anulable entonces Yi = xi
(b) Si x ∈ Vλ , entonces se toma Yi como xi y como λ
(c) No añadir ninguna producción A → λ
3. Si λ ∈ L(G) entonces VN = VN y S = S.
En otro caso,
(a) si S no aparece en la parte derecha
i. Añadir la producción S → λ
ii. VN = VN y S = S
(b) en otro caso
i. VN = VN {S }, siendo S el nuevo símbolo inicial
ii. Añadir a P S → S|λ
´ ´
Analisis Sintactico – p.56/64
57. Eliminación de producciones unitarias
Definición 18 Llamamos producciones unitarias a las que tienen la
forma A → B, con A, B ∈ VN .
Teorema 5 Dada una g.l.c. G = (VN , VT , S, P ) existe una g.l.c.
equivalente G = (VN , VT , S, P ) que no contiene producciones unitarias.
El algoritmo para calcular G’ es el siguiente:
Algoritmo 8 1. Suponemos que G es λ-libre; si no es así, se
transforma según el algoritmo 3
2. Para cada A ∈ VN se calcula VV (A) = {B ∈ VN |A ⇒+ B}.
3. P = Producciones no unitarias de P .
4. Para cada A ∈ VN tal que VV (A) = ∅.
Para cada B ∈ VV (A)
Para cada B → β ∈ P (no unitaria)
Añadir A → β a P
´ ´
Analisis Sintactico – p.57/64
58. Gramática libre de ciclos
Definición 19 Una gramática libre de ciclos es aquella que no contiene
derivaciones de la forma A ⇒∗ A.
Definición 20 Una gramática es propia si no tiene símbolos inútiles, es
λ-libre y libre de ciclos.
Para convertir una gramática en otra equivalente propia, podemos seguir
los siguientes pasos:
1. Pasar la gramática a una equivalente λ-libre.
2. Eliminar las producciones unitarias (no hay ciclos).
3. Eliminar símbolos inútiles.
No debemos olvidar que una gramática puede tener producciones uni-
tarias y ser propia.
´ ´
Analisis Sintactico – p.58/64
59. Recursividad en las gramáticas
Definición 21 Una gramática G = (VN , VN , P, S) es recursiva por la izquierda (derecha) si
+ +
existe un A ∈ VN tal que existe una derivación A =⇒ Aα (A =⇒ αA) para alguna cadena
α.
Definición 22 Una gramática G = (VN , VN , P, S) es recursiva si existe un A ∈ VN tal que
+
existe una derivación A =⇒ αAβ.
Algoritmo 9 Eliminación de la recursividad inmediata por la izquierda.
Entrada: Un conjunto de producciones {pi /pi ∈ P } con el no terminal A ∈ VN como parte
izquierda de una gramática G CFG sin λ-producciones.
Salida: Un nuevo conjunto de producciones sin recursividad inmediata por la izquierda.
1. Ordénense las producciones de Ai en la forma
A → Aα1 |Aα2 | · · · |Aαm |β1 |β2 | · · · |βn
en donde ninguna βi comienza con A.
2. Sustituir todas las producciones de A por
A → β1 A |β2 A | · · · |βn A
A → α1 A |α2 A | · · · |αm A |λ
3. La salida es el conjunto de nuevas producciones obtenidas en el paso anterior.
´ ´
Analisis Sintactico – p.59/64
60. Recursividad en las gramáticas (III)
Algoritmo 10 Eliminación de la recursividad por la izquierda.
Entrada: La gramática G propia
Salida: Una gramática equivalente, sin recursividad por la izquierda.
1. Ordénense los Ai ∈ VN en un orden A1 , A2 , . . . , An .
2. for i:=1 to n do
begin
forj:= 1 to i − 1 do
sustituir cada producción de la forma Ai → Aj γ
por las producciones Ai → δ1 γ|δ2 γ| · · · |δk γ, en
donde Aj → δ1 |δ2 | · · · |δk es el conjunto de
producciones actuales del no terminal Aj ;
Además, eliminar la recursividad inmediata por la
izquierda de las producciones de Ai .
end
´ ´
Analisis Sintactico – p.60/64
61. Forma normal de Chomsky
Definición 23 Sea una CFG G = (VN , VT , P, S). Se dice que
G está en Forma Normal de Chomsky (CNF), si toda
producción de P está en una de las formas siguientes:
1. A → BC, en donde A, B y C están en VN ,
2. A → a, en donde A ∈ VN y a ∈ VT ,
3. Si λ ∈ L(G) entonces S → λ está en P y además S no
aparece en la parte derecha de ninguna producción de P .
´ ´
Analisis Sintactico – p.61/64
62. Forma normal de Chomsky (II)
Algoritmo 11 Conversión a Forma Normal de Chomsky.
Entrada: Una gramática CFG propia, G = (VN , VT , P, S) sin producciones simples.
Salida: Una gramática G en forma CNF, tal que L(G) = L(G ).
Método: Sea el conjunto P formado por las producciones siguientes:
1. Añadir toda producción en la forma A → a ∈ P
2. Añadir toda producción en la forma A → AB ∈ P
3. Si S → λ ∈ P entonces añadirla también.
4. Para cada producción en la forma A → X1 · · · Xk con k > 2, añadir las producciones resultantes a
continuación: asumimos que Xi representa a Xi si Xi ∈ VN , y Xi es un nuevo no-terminal si Xi ∈ VT . Las
nuevas producciones serán las siguientes:
A → X 1 < X2 · · · X k >
< X2 · · · Xk >→ X2 < X3 · · · Xk >
···
< Xk−2 · · · Xk >→ Xk−2 < Xk−1 Xk >
< Xk−1 Xk >→ Xk−1 Xk
en donde cada < Xi · · · Xk > es un nuevo símbolo no-terminal.
5. Para cada producción de la forma A → X1 X2 en donde bien X1 o X2 ó los dos están en VT , añadir
A → X 1 X2 a P
6. Para cada a introducido en los pasos 4 y 5, añadir a → a. Sea VN igual a VN más todos los nuevos
no-terminales introducidos en los pasos anteriores.
La nueva gramática G = (VN , VT , P , S) es la deseada.
´ ´
Analisis Sintactico – p.62/64
63. Forma normal de Chomsky (III)
Veamos la aplicación del algoritmo con la gramática CFG cuyo conjunto
P está formado por las siguientes producciones:
S → aAB|BA
A → BBB|a
B → AS|b
En el nuevo conjunto P introducimos las producciones S → BA, A → a y
B → AS|b. La producción S → aAB generará el siguiente grupo de
nuevas producciones
S → a < AB >
< AB >→ AB
a →a
que irán a parar a P .
´ ´
Analisis Sintactico – p.63/64
64. Forma normal de Chomsky (IV)
Para la producción A → BBB, se generarán las siguientes nuevas producciones
A → B < BB >
< BB >→ BB
que también se introducirán en P . La nueva gramática, G = (VN , VT , P , S) quedará, con
VN = {S, A, B, < BB >, < AB >, a }, y P contendrá las siguientes producciones:
S → a < AB > |BA
A → B < BB > |a
B → AS|b
< AB >→ AB
< BB >→ BB
a →a
´ ´
Analisis Sintactico – p.64/64