Construcción de las Tablas de Análisis Sintáctico LR(1) - No

Anuncio
Algoritmos usados por el Generador de Analizadores Sintácticos
Capítulo 4: Algoritmos usados por el Generador de Analizadores
Sintácticos
4.1
Introducción
En este capítulo se presentan los algoritmos usados por el Generador de
Analizadores Sintácticos SLR. Se tratará de clarificar bien los conceptos
teóricos utilizados, lo que no hace la bibliografía que se utilizó (luego de la
lectura de la bibliografía, en el mejor de los casos quedan muchos conceptos
flotando y sin relación alguna, y lo más común es que no se los entienda).
Los analizadores sintácticos generados trabajan con la técnica SLR(1) o
SLR para simplificar, la que es un caso especial de LR(k). A diferencia de la
técnica LR(1) canónica y de la técnica LALR, esta técnica genera tablas
mucho más pequeñas (se minimiza el número de estados del autómata de pila).
Se la puede aplicar a casi todas las gramáticas de contexto libre que pueda
necesitar un programador (el desarrollo de lenguajes de programación con
gramáticas complejas puede requerir trabajar con la técnica LALR, pero si la
técnica SLR es aplicable entonces el analizador será más eficiente).
La técnica LR ha sido descripta por Knuth [1965], pero no resultó
práctica porque el tamaño de las tablas que se debían construir eran demasiado
grandes (recordar que en ese tiempo 16 kbytes era mucha memoria). En 1969
Korenjak mostró que mediante esa técnica se podían producir analizadores
sintácticos de tamaño razonable para las gramáticas de los lenguajes de
programación. En 1969 y 1971, DeRemer inventó los métodos "LR simples"
(SLR por Simple LR) y "LR con símbolo de anticipación" (LALR por
lookahead-LR) que son más simples que los de Korenjak.
La utilización de la técnica SLR (y del resto de las LR) simplifica
mucho el trabajo del desarrollo de un traductor, siempre y cuando se disponga
de un programa generador de las tablas de análisis sintáctico. Si no se dispone
de un generador de las tablas, la construcción de las mismas puede llegar a
desalentar la construcción del traductor puesto que se requiere un dominio
profundo de los algoritmos de construcción y de la teoría en la que se basan.
Una persona puede entender bien los algoritmos de construcción de las tablas,
y aún así cometer muchos errores.
28
Algoritmos usados por el Generador de Analizadores Sintácticos
El analizador SLR es uno solo para todas las posibles gramáticas. Es un
autómata de pila determinístico, y su funcionamiento es análogo al autómata
finito determinístico. El algoritmo se lo ilustra en la sección siguiente.
La tabla construida para un analizador SLR se llama tabla SLR, la
gramática para la cual se pueda construir una tabla SLR se llamará gramática
SLR. Una gramática para la cual la construcción de la tabla SLR genere
conflictos no es SLR, aunque se puedan resolver a mano los conflictos.
4.2
Análisis sintáctico por la técnica SLR (LR Simple)
Antes de explicar cómo se construyen las tablas es necesario ver
primero cómo se las usa.
4.2.1
Algoritmo de análisis sintáctico SLR
Se dispone de una pila en la que se pueden almacenar estados (enteros
no negativos) o símbolos de la gramáticas (terminales o no terminales).
También se dispone de un par de tablas a las que se les da el nombre de
acción e ir_a.
La tabla acción contiene las acciones que puede ejecutar el autómata.
Las acciones se especifican con una letra que especifica la acción y el número
de estado al que debe ir el autómata luego de ejecutarla. Las mismas pueden
ser:
1. Desplazamiento: se simboliza con la letra d y un número que indica el
estado al que debe pasar el autómata.
2. Reducción: se simboliza con la letra r y un número que indica el número
de regla por la cual se reduce.
3. Aceptar: se simboliza con la letra a. No tiene un número de sufijo puesto
que no hace falta; el autómata se detiene automáticamente al encontrar
esa orden.
4. Nada: si una entrada en esta tabla no contiene alguna de las tres
alternativas anteriores entonces en la posición actual hay error de
sintaxis.
29
Algoritmos usados por el Generador de Analizadores Sintácticos
La tabla ir_a contiene números enteros no negativos que son los estados
a los que tiene que ir el autómata luego de ejecutar una reducción.
La figura siguiente muestra el algoritmo de análisis sintáctico LR
simple:
Se dispone de una pila vacía en la cual se coloca 0 como estado inicial.
Sea pt el puntero al primer terminal de la cadena de entrada.
Salir = No
Repetir
Sea E el estado en el tope de la pila y t el terminal apuntado por pt.
Si
acción[E, t] = dY entonces
Apilar primero t y luego Y.
Avanzar pt al siguiente terminal.
Sino, si acción[E, t] = rX entonces
X es el número de regla por la que hay que reducir, entonces desapilar
2 veces el número de símbolos de la parte derecha de la regla número X.
Sea Y el estado que ahora está en el tope de la pila y A el no terminal de
la parte derecha de la regla número X.
Apilar primero A y luego ir_a[X, A].
Sino, si acción[E, t] = a entonces
Se ha llegado al estado de aceptación.
Salir = Si.
Aceptar = Si.
Sino
Salir = Si.
Aceptar = No; hay error de sintaxis.
FinSi
Hasta que Salir = Si.
Si Aceptar = Si entonces
La oración de entrada cumple con la estructura definida por la gramática,
esto es, es gramatical.
Sino La oración de entrada no es gramatical.
30
Algoritmos usados por el Generador de Analizadores Sintácticos
Fig. 10 Algoritmo de análisis sintáctico SLR
4.2.2 Ejemplo del funcionamiento del analizador sintáctico SLR
Para ejemplificar el funcionamiento del analizador sintáctico
trabajaremos con la gramática siguiente (las reglas han sido numeradas):
1:
2:
3:
4:
5:
6:
7:
P
D
D
L
L
I
I
-->
-->
-->
-->
-->
-->
-->
D I
'Var' L ':' 'Tipo' ';'
L ',' 'Id'
'Id'
I 'Instr' ';'
Las tablas de análisis sintáctico SLR para la gramática dada son las
siguientes:
| acción(estado, símbolo terminal)
| ir_a(estado, símbolo no
|
|
terminal)
Est | 'Var' ':' 'Tipo' ';' ',' 'Id' 'Instr' FA | P D L I
----+--------------------------------------------+----------------------0 |
d1
·
·
·
·
·
r3 r3 | 2 3 · ·
1 |
·
·
·
·
·
d4
·
· | · · 5 ·
2 |
·
·
·
·
·
·
·
a | · · · ·
3 |
·
·
·
·
·
·
r7 r7 | · · · 6
4 |
· r5
·
· r5
·
·
· | · · · ·
5 |
· d7
·
· d8
·
·
· | · · · ·
6 |
·
·
·
·
·
·
d9 r1 | · · · ·
7 |
·
·
d10
·
·
·
·
· | · · · ·
8 |
·
·
·
·
· d11
·
· | · · · ·
9 |
·
·
· d12
·
·
·
· | · · · ·
10 |
·
·
· d13
·
·
·
· | · · · ·
11 |
· r4
·
· r4
·
·
· | · · · ·
12 |
·
·
·
·
·
·
r6 r6 | · · · ·
13 |
·
·
·
·
·
·
r2 r2 | · · · ·
Fig. 11 Tabla de análisis sintáctico SLR para la gramática de un
lenguaje simple.
En la tabla acción el símbolo FA significa Fin de Archivo. Aunque no
forma parte del texto de entrada, su inclusión fue realizada durante la
construcción de las tablas; el símbolo Fin de Archivo es un símbolo terminal
de la gramática ampliada (ver más adelante qué es una gramática ampliada).
Sea el texto de entrada el siguiente:
Var Id : Tipo ; Instr ;
31
Algoritmos usados por el Generador de Analizadores Sintácticos
El análisis sintáctico se ilustra en el cuadro siguiente. Se utiliza un
espacio como separador de elementos de la pila. Si se ha llegado al fin de
archivo (FA) en la columna Entrada no habrán terminales.
Paso
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Pila
0
0 Var 1
0 Var 1 Id 4
0 Var 1 L
0 Var 1 L 5
0 Var 1 L 5 : 7
0 Var 1 L 5 : 7 Tipo 10
0 Var 1 L 5 : 7 Tipo 10 ; 13
0D
0D3
0D3I
0D3I6
0 D 3 I 6 Instr 9
0 D 3 I 6 Instr 9 ; 12
0D3I
0D3I6
0P
0P2
Entrada
Var Id : Tipo ; Instr ;
Id : Tipo ; Instr ;
: Tipo ; Instr ;
: Tipo ; Instr ;
: Tipo ; Instr ;
Tipo ; Instr ;
; Instr ;
Instr ;
Instr ;
Instr ;
Instr ;
Instr ;
;
Acción
d1
d4
r5
Ir a 5
d7
d10
d13
r2
Ir a 3
r7
Ir a 6
d9
d12
r6
Ir a 6
r1
Ir a 2
Acepta
r
Fig. 12 Ejemplo de análisis sintáctico con la técnica SLR.
La pila del analizador puede quedar con muchos elementos y aún así
haber llegado al estado de aceptación.
4.2.3 Clasificación de los errores que se pueden producir durante el
análisis sintáctico
El algoritmo de análisis sintáctico SLR presentado en la figura 10 no
especifica qué tipos de errores pueden aparecer ni cómo tratarlos. En esta
32
Algoritmos usados por el Generador de Analizadores Sintácticos
sección se dará una definición de los tipos de errores que pueden aparecer ya
que la bibliografía no da una definición rigurosa de los mismos.
Error lexicográfico: se dará este nombre al error que se produce al intentar
buscar un símbolo terminal siguiente al actual y se encontró un símbolo que no
es parte del conjunto de símbolos terminales de la gramática. El Fin de
Archivo (FA) no es error lexicográfico, sino que simplemente no hay más
símbolos terminales de entrada.
Error semántico: si la implementación del analizador sintáctico incluye la
posibilidad de ejecutar acciones semánticas (ver sección 2.3.8 y 12.3.3),
durante la ejecución de esas acciones se pueden producir errores. A esos
errores se les dará el nombre de errores semánticos.
Error sintáctico: si no es error lexicográfico ni error semántico entonces es
sintáctico. El autómata en este caso se queda sin saber qué acción ejecutar (la
entrada de la tabla acción no está definida para el estado actual y el terminal
de entrada).
Las definiciones precedentes son aplicables a todas las técnicas de
análisis que hay esta el momento de la escritura de este trabajo.
En el contexto de las técnicas LR(k), un error semántico puede ser
ignorado sin que se produzcan efectos indeseados. No ocurre lo mismo con los
errores lexicográficos y sintácticos (nadie sabe con certeza lo que quiso
escribir el autor del texto fuente), aunque los efectos colaterales pueden
reducirse previo estudio y modificación "a mano" de las tablas.
4.3
Fundamentos de la construcción de las tablas de análisis SLR
Antes de explicar la construcción de las tablas se darán primero algunas
definiciones, se explicarán algoritmos usados por el algoritmo de construcción
de las tablas de análisis sintáctico SLR y se explicarán los fundamentos de la
construcción de las tablas del analizador SLR.
4.3.1 Elemento del análisis sintáctico LR(0)
Se denominará elemento del análisis sintáctico LR(0) (elemento para
abreviar) de una gramática G a una regla de G con un punto en alguna posición
de la parte derecha.
33
Algoritmos usados por el Generador de Analizadores Sintácticos
Una regla que derive en la cadena nula (A --> ) genera un solo elemento
que es "A --> . ".
Intuitivamente, un elemento indica hasta dónde se ha visto una
producción en un momento dado del proceso de análisis sintáctico. Por
ejemplo, la regla A --> B C D genera cuatro elementos:
A
A
A
A
--->
--->
--->
--->
.
B
B
B
B
.
C
C
C
C
.
D
D
D
D
.
El primer elemento indica que a continuación se espera ver en la entrada
una cadena derivable de "B C D". El segundo elemento indica que se acaba de
ver en la entrada una cadena derivable de B, y que a continuación se espera ver
una cadena derivable de "C D".
4.3.2 Operación Cerradura
Sea I un conjunto de elementos para una gramática G, Cerradura(I) es
el conjunto de elementos construidos a partir de I y de G mediante las dos
siguientes reglas:
1. Inicialmente, todo elemento de I se agrega a Cerradura(I).
2. Si A --> α . B β está en Cerradura(I) y B --> γ es una regla, entonces se
agrega B --> . γ a Cerradura(I), si todavía no fue agregado. Se aplica esta
regla hasta que no se puedan agregar más elementos a Cerradura(I).
Intuitivamente, si A --> α . B β está en Cerradura(I) es porque en algún
momento del proceso de análisis sintáctico se cree que es posible ver a
continuación una cadena derivable de B como entrada. Si B --> γ es una regla,
también se espera ver una subcadena derivable de B --> γ en éste punto, y por
esta razón se incluye B --> . γ en Cerradura(I).
Ejemplo: para la gramática utilizada en el punto 4.2.2, sea
I = { (P --> • D I) } entonces Cerradura(I) contiene los siguientes elementos:
P ---> • D I
D ---> • 'Var' L ':' 'Tipo' ';'
D ---> •
34
Algoritmos usados por el Generador de Analizadores Sintácticos
Otro ejemplo: para la gramática utilizada en el punto 4.2.2, sea
I = { ( D ---> 'Var' • L ':' 'Tipo' ';' ) }, entonces Cerradura(I) contiene los
siguientes elementos:
D ---> 'Var' • L ':' 'Tipo' ';'
L ---> • L ',' 'Id'
L ---> • 'Id'
4.3.3 Mangos
Informalmente, un mango de una cadena es una subcadena que
concuerda con el lado derecho de una regla y cuya reducción al no terminal del
lado izquierdo de la regla representa un paso a lo largo de la inversa de una
derivación por la derecha.
Formalmente, un mango de una forma de frase derecha γ es una regla
A-->β y una posición de γ donde la cadena β podría encontrarse y sustituirse
por A para producir la forma de frase derecha previa en una derivación por la
derecha de γ. Es decir, si O ==>* αAω ==>* αβω, entonces A-->β si la
posición que sigue de α es un mango de αβω. La cadena ω a la derecha del
mango contiene sólo símbolos terminales.
Si la gramática no es ambigua entonces toda forma de frase derecha de
la gramática tiene exactamente un mango.
Para la gramática del ejemplo dado en la sección 4.2.2, obsérvesen la
figura 12, la fila correspondiente al paso 14: la forma de frase derecha es
D I Instr ;
la operación que se va a ejecutar en ese paso es r6 y el mango es "I Instr ;",
que concuerda con la parte derecha de la regla 6 ( I --> I 'Instr' ';' ). En este
caso, a la derecha del mango no hay símbolos terminales.
4.3.4 Prefijo Viable
Los prefijos de las formas de frase derecha que pueden aparecer en la
pila de un analizador sintáctico por desplazamiento y reducción se denominan
prefijos viables.
Una definición equivalente de un prefijo viable es la de que es un prefijo
de una forma de frase derecha que no continúa más allá del extremo derecho
del mango situado más a la derecha de esta forma de frase. Con esta
35
Algoritmos usados por el Generador de Analizadores Sintácticos
definición, siempre es posible añadir símbolos terminales al final de un prefijo
viable para obtener una forma de frase derecha. Por lo tanto, aparentemente no
hay error siempre que la porción examinada de la entrada hasta un punto dado
pueda reducirse a un prefijo viable.
Por ejemplo, para la gramática del ejemplo dado en la sección 4.2.2,
obsérvese en la figura 12, la fila correspondiente al paso 8, la forma de frase
derecha es:
Var L : Tipo ; Instr ;
y se reduce por regla 2 el mango "Var L : Tipo ;" para obtener la forma de
frase derecha siguiente:
D Instr ;
Aquí, D es un prefijo viable.
4.3.5 Operación ir_a
La función ir_a(I, X) donde I es un conjunto de elementos y X es un
símbolo de la gramática (terminal o no terminal) se define como la cerradura
del conjunto de todos los elementos A --> α X . β tales que A --> α . X β
esté en I.
Intuitivamente, si I es el conjunto de elementos válidos para algún
prefijo viable, entonces ir_a(I, X) es el conjunto de elementos válidos para el
prefijo viable γX.
Por ejemplo, para la gramática del ejemplo dado en la sección 4.2.2,
para el conjunto de elementos I igual a:
P ---> • D I
D ---> • 'Var' L ':' 'Tipo' ';'
D ---> •
entonces ir_a(I, D) consta de:
P ---> D • I
I ---> • I 'Instr' ';'
I ---> •
36
Algoritmos usados por el Generador de Analizadores Sintácticos
4.3.6 Colección Canónica de Conjuntos de Elementos del Análisis
Sintáctico LR(0)
La construcción de la colección canónica de conjunto de elementos
LR(0) se aplica a una gramática aumentada G' obtenida a partir de G por el
agregado de la siguiente regla:
O' --> O
donde O es el símbolo inicial de la gramática G. Ahora O' es el símbolo inicial
de la gramática G', a la que se llamará gramática aumentada. El objetivo de
esta regla es la de indicar al analizador cuándo debe detener el análisis
sintáctico y anunciar la aceptación de la cadena. La aceptación se produce
únicamente cuando el analizador está por reducir O' --> O.
El algoritmo se muestra en la siguiente figura, en donde C es conjunto
de conjuntos de elementos (la colección canónica LR(0) ):
Sea C = { Cerradura( { O' --> . O } ) }
Repetir
Para cada conjunto de elementos I en C y cada símbolo gramatical X tal que
ir_a(I, X) no esté vacío y no esté en C hacer
Agregar ir_a(I, X) a C.
FinPara
Hasta que no se puedan agregar más conjuntos de elementos a C.
Fig. 13 Construcción de la Colección Canónica LR(0)
En adelante, a cada I se lo llamará indistintamente conjunto de
elementos LR(0) o estado.
Ejemplo: para la gramática dada en la sección 4.2.2, la gramática
aumentada es:
P' -->
P --->
D --->
D --->
L --->
L --->
I --->
I --->
P
D I
'Var' L ':' 'Tipo' ';'
L ',' 'Id'
'Id'
I 'Instr' ';'
37
Algoritmos usados por el Generador de Analizadores Sintácticos
y la colección canónica es:
Estado 0 (I0):
P' ---> • P
P ---> • D I
D ---> • 'Var' L ':' 'Tipo' ';'
D ---> •
Estado
D --->
L --->
L --->
1 (I1):
'Var' • L ':' 'Tipo' ';'
• L ',' 'Id'
• 'Id'
Estado 2 (I2):
P' ---> P •
Estado
P --->
I --->
I --->
3:
D • I
• I 'Instr' ';'
•
Estado 4:
L ---> 'Id' •
Estado 5:
D ---> 'Var' L • ':' 'Tipo' ';'
L ---> L • ',' 'Id'
Estado 6:
P ---> D I •
I ---> I • 'Instr' ';'
Estado 7:
D ---> 'Var' L ':' • 'Tipo' ';'
Estado 8:
L ---> L ',' • 'Id'
Estado 9:
I ---> I 'Instr' • ';'
Estado 10:
D ---> 'Var' L ':' 'Tipo' • ';'
Estado 11:
L ---> L ',' 'Id' •
Estado 12:
I ---> I 'Instr' ';' •
Estado 13:
D ---> 'Var' L ':' 'Tipo' ';' •
4.3.7 Idea Central del Método SLR
Los elementos del análisis sintáctico LR(0) pueden considerarse como
los estados del autómata finito no determinista que reconoce los prefijos
viables. Para poder convertir el autómata en determinista hace falta agrupar los
38
Algoritmos usados por el Generador de Analizadores Sintácticos
elementos en conjuntos. Los conjuntos serían entonces los estados del
autómata finito determinista que reconoce los prefijos viables.
La idea central del método SLR es construir primero a partir de la
gramática un AFD que reconozca los prefijos viables. Se construye la
colección canónica de elementos LR(0). Cada conjunto de elementos dentro de
la colección canónica pasan a ser los estados del analizador sintáctico SLR
(del AFD que el mismo usa).
Para el ejemplo dado en el punto anterior, el AFD que reconoce prefijos
viables se muestra en la figura siguiente:
'Id'
ar'
'V
0 P
1
4
L
':'
5
2
7
','
D
'Tipo'
'Id'
8
3
I
6
'Instr'
9
';'
10
';'
13
11
12
Fig. 14 AFD que reconoce prefijos viables de la gramática del
ejemplo.
El AFD de la figura precedente tiene dos problemas: no tiene estados
finales y tiene transiciones con símbolos no terminales.
El hecho de tener transiciones con no terminales hace necesario
disponer de una pila para la implementación del autómata. El autómata
resultante se pasa a llamar autómata de pila, que como ya se mencionó en el
punto 2.2.5, son los que implementan analizadores sintácticos para gramáticas
del Tipo 2 de la clasificación de Chomsky.
4.3.8 Función Anulable
La función Anulable se aplica a un símbolo no terminal de una
gramática y devuelve Verdadero si ese símbolo es anulable.
Un símbolo no terminal A es anulable si de alguna forma, mediante
derivaciones sucesivas con las reglas de la gramática, se transforma A en la
39
Algoritmos usados por el Generador de Analizadores Sintácticos
cadena nula ( A ==>* λ ). (λ representa a la cadena nula. En este trabajo
cuando en una regla se quiere denotar la presencia de la cadena nula no se
pone nada puesto que es más claro.)
Definiremos primero una versión simple de Anulable a la que se le
llamará AnulableSimple. Esta función devolverá Verdadero si hay una regla
que derive el no terminal directamente en la cadena nula ( A --> ).
A partir de la función de AnulableSimple se define la función Anulable
de la siguiente manera: un símbolo no terminal X de una gramática G es
anulable si:
1. X es Anulable Simple ( X --> ).
2. Si existe alguna regla X --> Y Z, con Y y Z anulables.
El punto 2 hace que la definición sea recursiva, por lo tanto un
algoritmo que la implemente podrá ser recursivo, lo que no es aconsejable
debido a la cantidad de veces que se puede repetir el cálculo de Anulable para
algunos símbolos no terminales.
Ejemplo: para la gramática siguiente
O
A
A
B
B
C
C
--->
--->
--->
--->
--->
--->
--->
A B C
a B
b C
C
c O
El cálculo de AnulableSimple para cada uno de los símbolos daría el
siguiente resultado:
O:
A:
B:
C:
no
si
no
si
es
es
es
es
anulable
anulable
anulable
anulable
simple.
simple.
simple.
simple.
El cálculo de Anulable arroja el siguiente resultado:
O:
A:
B:
C:
si
si
si
si
es
es
es
es
anulable
anulable
anulable
anulable
porque
porque
porque
porque
A, B y C son anulables.
es anulable simple.
hay una regla B --> C, y C es anulable.
es anulable simple.
4.3.9 La operación Primero
Si α es una cadena de símbolos gramaticales, se considera Primero(α)
como el conjunto de terminales que inician las cadenas derivadas de α.
40
Algoritmos usados por el Generador de Analizadores Sintácticos
Si α ==>* λ, entonces λ también está en Primero(α). El resultado de la
función Primero es un conjunto de símbolos terminales, que obviamente es un
subconjunto de T (el conjunto de símbolos terminales de la gramática).
Para calcular Primero(X) para todos los símbolos gramaticales X, se
proponen las siguientes reglas:
1. Si X es terminal, entonces Primero(X) es { X }.
2. Si X -- > λ es una regla, entonces agregar λ a Primero(X).
3. Si X es no terminal y X --> Y1 Y2 ... Yk entonces se agrega b a
Primero(X) si, para alguna i, b está en Primero(Yi) y λ está en todos
los conjuntos Primero(Y1), ..., Primero(Yi - 1); esto es, la cadena
Y1...Yi - 1 ==>* λ.
Si λ está en Primero(Yj) para toda j = 1, 2, ..., k, entonces se agrega
λ a Primero(X); esto es, la cadena Y1...Yk ==>* λ.
Por ejemplo, todo lo que está en Primero(Y1) sin duda estará en
Primero(X). Si Y1 no deriva en λ, entonces no se agrega nada más a
Primero(X), pero si Y1 ==>* λ, entonces se agrega Primero(Y2), y
así sucesivamente.
4. Se repetirán las tres primeras reglas hasta que no se puedan agregar
Fig. 15 Cálculo de Primero(X).
Observando la regla 1, se concluye que el cálculo de Primero(X) para X
terminal no tiene sentido, puesto que es un axioma.
A los efectos de la utilización de la función Primero en la generación de
las tablas de análisis sintáctico SLR, el agregado de λ no es necesario. En los
ejemplos siguientes no se incluye λ en donde se debiera.
Ejemplo 1: para la gramática dada en la sección anterior (4.3.8) los
conjuntos Primero sin Lambda (λ) son:
Primero(O)
Primero(A)
Primero(B)
Primero(C)
=
=
=
=
{
{
{
{
a
a
b
c
b c }
}
c }
}
41
Algoritmos usados por el Generador de Analizadores Sintácticos
Ejemplo 2: para la gramática dada en la sección (4.2.2) los conjuntos
Primero sin Lambda (λ) son:
Primero(P)
Primero(D)
Primero(L)
Primero(I)
=
=
=
=
{
{
{
{
'Var' 'Instr' }
'Var' }
'Id' }
'Instr' }
4.3.10 La operación Siguiente
La función Siguiente se calcula para los símbolos no terminales de la
gramática, únicamente.
Para calcular Siguiente(X) para todos los símbolos no terminales X, se
proponen las siguientes reglas:
1. Agregar FA (Fin de Archivo) a Siguiente(O), donde O es el símbolo
inicial y FA es el delimitador derecho de la entrada.
2. Si hay una regla A ---> α B β, entonces todo lo que esté en
Primero(β) excepto λ se agrega a Siguiente(B).
3. Si hay una regla A ---> α B o una regla A ---> α B β, donde
Primero(β) contenga λ (es decir, β ==>* λ), entonces todo lo que
esté en Siguiente(A) se agrega a Siguiente(B).
Fig. 16 Cálculo de Siguiente(X).
Obsérvese las reglas 2 y 3, al decir Primero(β) se está hablando en
realidad de la unión de varios conjuntos Primero(Xi) para i = 1, ..., k y todos
los Primero(Xi) excepto Primero(Xk) contiene a λ. Por lo tanto, se deberá tener
esto en cuenta al implementar el algoritmo.
Obsérvese también que en la regla 2, al agregar todo lo que esté en
Primero(β) excepto λ a Siguiente(B) justifica el hecho de que en este trabajo
se calcula Primero(X) sin λ.
Ejemplo 1: para la gramática dada en la sección anterior (4.3.8) los
conjuntos Siguiente(X) son los siguientes:
Siguiente(O) = { c b }
Siguiente(A) = { b c }
42
Algoritmos usados por el Generador de Analizadores Sintácticos
Siguiente(B) = { c b }
Siguiente(C) = { c b }
Ejemplo 2: para la gramática dada en la sección (4.2.2) los conjuntos
Siguiente(X) son:
Siguiente(P)
Siguiente(D)
Siguiente(L)
Siguiente(I)
4.4
=
=
=
=
{
{
{
{
}
'Instr' }
':' ',' }
'Instr' }
Construcción de las tablas de análisis SLR
La construcción de las tablas de análisis sintáctico SLR (similar a la
mostrada en la figura 11 de la sección 4.2.2) se resume en el algoritmo
mostrado en la figura siguiente.
Si los 3 primeros pasos generan acciones contradictorias, se dice que la
gramática no es SLR(1) porque el algoritmo no consigue producir las tablas
para el análisis sintáctico. En este caso no se ejecutan los pasos siguientes.
43
Algoritmos usados por el Generador de Analizadores Sintácticos
1. Construir G', la gramática aumentada, a partir de G, por el
agregado de la regla O' --> O, donde O era el símbolo inicial de
G. O' pasa a ser el símbolo inicial en G'.
2. Construir C = { I0, I1, ..., In }, la colección canónica de conjuntos
de elementos del análisis sintáctico LR(0) para la gramática
aumentada G'.
3. El estado i se construye a partir de Ii. Las acciones de análisis
sintáctico para el estado i se determinan como sigue:
a. Si A --> α . x β está en Ii con x terminal e ir_a(Ii, x) = Ij,
entonces asignar "desplazar j" a accion[i, x].
b. Si A --> α . está en Ii, con A distinto de O' entonces asignar
"reducir por A --> α" a accion[i, x] para todo x perteneciente a
Siguiente(A).
c. Si O' ---> O . está en Ii, entonces asignar "aceptar" a
accion[i, FA] (FA es el Fin de Archivo).
4. La tabla ir_a(i, X) para el estado i y el no terminal X se
construyen utilizando la regla:
si ir_a[Ii, X] = Ij entonces ir_a[i, X] = j
5. Todas las entradas de las tablas no definidas por las reglas 3 y 4
son consideradas "error".
6. El estado inicial del analizador es el que se construye a partir del
conjunto de elementos que contiene a S' ---> . S
Fig. 17 Construcción de las tablas de análisis sintáctico SLR.
Ejemplo 1: para la gramática dada en la sección 4.2.2, las tablas se
muestran en la figura 12 que se encuentra en la misma sección. La colección
canónica de elementos LR(0) los encontrará en la sección 4.3.6. Los conjuntos
Siguiente(X) los encontrará en la sección 4.3.10.
Ejemplo 2: para la gramática dada en la sección 4.3.8, los conjuntos
Siguiente(X) los encontrará en la sección 4.3.10. La colección canónica de
44
Algoritmos usados por el Generador de Analizadores Sintácticos
elementos LR(0) es la siguiente (aquí a los terminales se los encierra entre
comillas simples para que no queden dudas):
Estado 0:
O' ---> • O
O ---> • A B C
A ---> • 'a' B
A ---> •
Estado
A --->
B --->
B --->
C --->
C --->
1:
'a' • B
• 'b' C
• C
• 'c' O
•
Estado 2:
O' ---> O •
Estado
O --->
B --->
B --->
C --->
C --->
3:
A • B C
• 'b' C
• C
• 'c' O
•
Estado
B --->
C --->
C --->
4:
'b' • C
• 'c' O
•
Estado
C --->
O --->
A --->
A --->
5:
'c' • O
• A B C
• 'a' B
•
Estado 6:
A ---> 'a' B •
Estado 7:
B ---> C •
Estado
O --->
C --->
C --->
8:
A B • C
• 'c' O
•
Estado 9:
B ---> 'b' C •
Estado 10:
C ---> 'c' O •
Estado 11:
O ---> A B C •
Al construir la tabla acción obtenemos lo siguiente, en la que se
detectará 6 conflictos de desplazamiento-reducción:
acción(e, x)
'a' 'b' 'c'
ir_a(e, X):
FA
O
A
B
C
45
Algoritmos usados por el Generador de Analizadores Sintácticos
0
1
2
3
4
5
6
7
8
9
10
11
d1
·
·
·
·
d1
·
·
·
·
·
·
r3
d4
·
d4
r7
r3
r2
r5
r7
r4
r6
r1
r3
d5
·
d5
d5
r3
r2
r5
d5
r4
r6
r1
r3
r7
a
r7
r7
r3
r2
r5
r7
r4
r6
r1
2
·
·
·
·
10
·
·
·
·
·
·
3
·
·
·
·
3
·
·
·
·
·
·
· ·
6 7
· ·
8 7
· 9
· ·
· ·
· ·
· 11
· ·
· ·
· ·
Los conflictos los generó el paso 3.b del algoritmo y son los siguientes:
1. En el estado 1, con el terminal 'c' hay que elegir entre d5 o r7.
2. En el estado 1, con el terminal 'b' hay que elegir entre d4 o r7.
3. En el estado 3, con el terminal 'c' hay que elegir entre d5 o r7.
4. En el estado 3, con el terminal 'b' hay que elegir entre d4 o r7.
5. En el estado 4, con el terminal 'c' hay que elegir entre d5 o r7.
6. En el estado 8, con el terminal 'c' hay que elegir entre d5 o r7.
Debido a que esta gramática genera conflictos en la construcción de las
tablas SLR se dirá que la misma no es SLR. Una definición equivalente es
decir que la gramática es ambigua para la técnica de análisis SLR, lo que no
significa que sea ambigua a secas (la construcción unívoca de un árbol de
análisis sintáctico no es posible con la técnica SLR, esto es, el algoritmo de
construcción de las tablas se comporta en este caso como una función no
inyectiva porque puede generar varias tablas accion alternativas para la misma
gramática).
Más ejemplos de construcción de tablas las puede encontrar en el
capítulo 11 y 12 en donde se muestran ejemplos de ejecución de SLR1.
46
Descargar