Vous êtes sur la page 1sur 14

Práctica 2

Autómatas, Gramáticas y Lenguajes


2017-2018
Normas de entrega y calificación
La práctica tendrá una ponderación del 15 % de la nota definitiva de la
asignatura, siempre que se obtenga una nota superior o igual a 5 puntos en la
prueba presencial.
La práctica sólo podrá entregarse utilizando la aplicación de Tareas de los
cursos virtuales. La entrega de la misma será un archivo, en formato *.pdf,
nombrado como “Apellido1 Apellido2, Nombre (DNI).pdf”.
Esta práctica consiste en la realización de un cuestionario con ejercicios
teóricos-prácticos basados en los contenidos expuestos en el enunciado de esta
práctica.

Introducción
El objetivo de esta práctica es introducir una de las aplicaciones más
destacadas de los autómatas a pila. En concreto, se introducen algunos conceptos
básicos de los analizadores sintácticos LL(k) y LR(k). Para ello se deberá conocer
qué son los símbolos de preanálisis y el mecanismo de construcción de estos
analizadores. Estos conceptos pueden estudiarse con el material incluido en el
enunciado de esta práctica y en la referencia [1].
Estos analizadores se estudiarán en profundidad en la asignatura
“Procesadores de Lenguajes I” de tercer curso del grado de Ingeniería de
Informática.

Símbolos de preanálisis
Para poder utilizar en rutinas de programación los autómatas teóricos que se
definen en esta asignatura, es necesario garantizar el determinismo. No obstante,
dado un determinado autómata a pila no determinista, no siempre es posible
definir el autómata a pila determinista equivalente.
Sin embargo, existe una manera para poder eliminar el no determinismo en
algunos casos. Consiste en poder observar los siguiente símbolos que vienen
de la entrada pero sin llegar a consumirlos. Esto se conoce como principio de
preanálisis. De esta forma, en casos de no determinismo, el autómata puede
tomar decisiones sobre qué transición aplicar teniendo en cuenta los siguientes
k elementos de la entrada.
Por ejemplo, dada la gramática:

G = ({S}, {x, y}, S, P )

1
donde S es el símbolo inicial de la gramática y P es el siguiente conjunto de
producciones:

S → xSy
S→ǫ

A la hora de derivar esta gramática se puede utilizar la primera o la segunda


producción. Se trata de averiguar, dada una cadena, con qué derivación se ha
obtenido. Por ejemplo, la cadena xxyy se ha obtenido con la derivación: S →
S→xSy S→ǫ
xSy ====⇒ xxSyy ==⇒ xxyy.
Para saber qué producción utilizar en cada paso de la derivación, bastaría con
comprobar si el siguiente símbolo de la entrada es una x o una y. Si es una x
implica que se debe utilizar de nuevo la primera producción, mientras que si el
símbolo es una y, indica que se debe aplicar la segunda producción.
Veámoslo ahora de otra forma. Sea M el autómata a pila definido a
continuación y que es equivalente a la gramática G definida anteriormente:

M = ({i, p, q, f }, {x, y}, δ, i, {i, f })

donde la función de transición δ se define mediante el siguiente diagrama de


transiciones (se supone que la pila se encuentra inicialmente vacía):
ǫ, S; xSy
ǫ, S; ǫ
x, x; ǫ
y, y; ǫ

ǫ, ǫ; Z0 ǫ, ǫ; S ǫ, Z0 ; ǫ
i p q f

El autómata M es no determinista. En el estado q existe no determinismo si


en la cima de la pila se encuentra un símbolo S independientemente del símbolo
que contenga la entrada. El símbolo de preanálisis ayuda a decidir qué transición
elegir de entre estas dos:

δ(q, ǫ; S) = (q, xSy) y δ(q, ǫ; S) = (q, ǫ)

El número de símbolos de preanálisis necesarios dependerá de la gramática.


En el caso de esta gramática, si en la entrada viene un símbolo x, sabremos que
la producción a elegir será S → xSy, mientras que si es un símbolo y, sabremos
que la producción será S → ǫ, puesto que estaremos reconociendo los símbolos y
del final de la cadena.
Así, sea G la gramática definida a continuación:

2
G = ({S, T }, {x, y, z}, S, P )

donde S es el símbolo inicial de la gramática y P es el siguiente conjunto de


producciones:

S → xSz
S → xyT yz
T →ǫ

Para decidir entre la primera y la segunda producción, serán necesarios dos


símbolos de preanálisis ya que en ambas producciones comienzan por el mismo
símbolo x. Así, si tenemos como símbolos de preanálisis los símbolos xx, se
elegirá la priemra producción. Por el contrario, si se tienen los símbolos xy se
elegirá la segunda producción.
Consideramos ahora la siguiente gramática G = ({S, A, B}, {x, y, z}, S, P )
donde S es el símbolo inicial de la gramática y P es el siguiente conjunto de
producciones:

S → AB
A → xAy
A→ǫ
B → yBz
B → yz

Los símbolos de preanálisis son necesarios para decidir entre la segunda y


la tercera producción y entre la cuarta y la quinta producción. En el primer
caso basta un símbolo de preanálisis, pero para el segundo caso son necesarios
dos símbolos de preanálisis (si los símbolos de preanálisis son yy tomaremos la
cuarta producción y si los símbolos de preanálisis son yz tomaremos la quinta
producción).
Veamos un último ejemplo. Consideramos la gramática G =
({S}, {x, y, z}, S, P ) donde S es el símbolo inicial y P es el siguiente
conjunto de producciones:

S → zzSxx
S → zzSxy
S → zzSyy
S → zzSyx
S →ǫ

En este caso, la gramática es no determinista, en el sentido en que no es posible


decidir de manera determinista qué producción utilizar independientemente del

3
número de símbolos de preanálisis que se utilicen. Esto es así, porque para
poder decidir qué producción se ha utilizado, es necesario investigar los últimos
símbolos de la cadena que han sido precedidos por un número arbitrariamente
grande de símbolos z. Por tanto, esta gramática es no determinista y este
no determinismo no puede resolverse con ninguna cantidad de símbolos de
preanálisis.

Introducción a los analizadores sintácticos


Una de las principales aplicaciones de los autómatas a pila es la construcción
de analizadores sintácticos. Los analizadores sintácticos son parte fundamental en
la construcción de compiladores y su objetivo es el análisis de las estructuras del
programa escrito en un determinado lenguaje de programación.
Para la construcción de rutinas de análisis sintáctico es necesario apoyarse en
máquinas deterministas. A diferencia de lo que ocurría con los autómatas finitos,
el imponer determinismo a los autómatas a pila restringe el conjunto de lenguajes
que se pueden aceptar.
Para superar la limitación del no determinismo, se suele utilizar el llamado
principio de preanálisis visto en el apartado anterior.

Analizadores sintácticos LL(k) y LR(k)


Existen dos tipos diferentes de analizadores sintácticos: los analizadores
LL(k) y los analizadores LR(k). Ambos se basan en un número k de símbolos
de preanálisis, así, un analizador LL(1) tendrá en cuenta un único símbolo de
preanálisis, de la misma forma que un analizador LL(2) tendrá en cuenta dos
símbolos de preanálisis.
Ambos tipos de analizadores se basan en la gramática independiente del
contexto que genera el lenguaje a analizar. La diferencia estriba en el tipo
de derivación que se realice para obtener cadenas válidas. Así, si se realizan
derivaciones más a la izquierda, la cadena se genera derivando siempre el no
terminal que se encuentra más a la izquierda. El autómata a pila correspondiente,
analiza la cadena de entrada produciendo una derivación por la izquierda
conforme lee la cadena de izquierda a derecha. Los analizadores sintácticos
desarrollados de esta manera se conocen como analizadores sintácticos LL. La
primera L denota que el analizador lee su entrada de izquierda a derecha (left to
right); la segunda L indica que el objetivo del analizador sintáctico es producir
una derivación por la izquierda [1].
La construcción de un analizador sintáctico LL(k) se basa en el autómata a
pila equivalente a una determinada gramática (ver apartado 7.4 del libro base de

4
la asignatura).
Una tabla de análisis sintáctico para un analizador sintáctico LL(1) es una
matriz bidimensional. Las filas se etiquetan con los no terminales de la gramática
sobre la cual se basa el analizador sintáctico. Las columnas se etiquetan con los
terminales de la gramática más una columna adicional FDC (que representa la
marca de fin de cadena). El elemento (m, n) de la tabla indica la acción que debe
seguirse cuando el no terminal m aparece en la cima de la pila y el símbolo de
preanálisis es n [1].
Veamos un ejemplo de cómo construir una tabla de análisis sintáctico
para un analizador LL(1) que se corresponda con la gramática G =
({S, A, B}, {a, b, c}, S, P ) donde S es el símbolo inicial y P es el siguiente
conjunto de producciones:

S → cABc
A → aAa
A→c
B → bBb
B→c

Para esta gramática la tabla de analizador sintáctico LL(1) es la siguiente:

a b c FDC
S error error cABc error
A aAa error c error
B error bBb c error

Nota: Se deja como ejercicio, construir el autómata a pila equivalente a


la gramática que resulta del procedimiento explicado en el apartado 7.4 del
libro base, en el cuál se basa esta tabla,
No obstante, existen lenguajes dentro de los límites de los analizadores
sintácticos de pila que no puede reconocer ningún analizador sintáctico LL(k),
sin importar el número de símbolos de preanálisis que se consideren. Un ejemplo
es el lenguaje L = {xn : n ≥ 0}∪{xn y n : n ≥ 0}. L es un lenguaje independiente
del contexto determinista. Un ejemplo de gramática que genera L es:
S → xA|xBy|ǫ
A → xA|ǫ
B → xBy|ǫ
Cualquier gramática independiente del contexto que genere L debe permitir
que se derive un no terminal con una cadena que contiene sólo x’s o una
cadena que contiene una combinación equilibrada de x’s e y’s. Esto quiere decir

5
que existirán por lo menos dos reglas posibles a la hora de derivar este no
terminal. De la misma forma, cualquier analizador sintáctico LL(k), se enfrentará
al problema de decidir cuál de estas reglas será la que se aplique cuando surja
el no terminal en la cima de la pila. Por desgracia, independientemente del
número de símbolos de preanálisis, siempre podremos encontrar una cadena para
la cual no es posible detectar el número de x’s o la ausencia o no de y’s sin
considerar más de k símbolos de preanálisis. Esto es así, porque el lenguaje
contiene cadenas con un número arbitrariamente grande de símbolos x’s. Por
tanto, los analizadores sintácticos LL(k) sólo son adecuados para una subclase
de lenguajes independientes del contexto.
Para paliar las deficiencias de los analizadores LL(k), se definieron los
analizadores sintácticos LR(k), que van procesando la cadena de izquierda a
derecha (del inglés Left to right derivation) mientras van construyendo una
derivación por la derecha (del inglés Right derivation) utilizando k símbolos de
preanálisis.
La construcción de un analizador sintáctico LR(k) para una determinada
gramática, consiste en la construcción de un autómata a pila equivalente a dicha
gramática y en la construcción de una tabla de análisis sintáctico que tiene en
cuenta los símbolos de preanálisis que sean necesarios. Dicha tabla se construye
a su vez, a partir de un autómata finito que tiene en cuenta en qué momento de la
derivación de una cadena nos encontramos en cada momento.
El proceso de construcción de estos elementos se encuentra definido en el
apartado 2.5 y en el apéndice A de [1].

El límite de los analizadores sintácticos LR(k) es el conjunto de lenguajes


independientes del contexto deterministas

A continuación mostramos un ejemplo concreto de construcción de un


analizador sintáctico LR(k).

Ejemplo de construcción de un analizador sintáctico LR(k)


En este apartado mostramos un ejemplo de construcción de un analizador
sintáctico LR(k) tomado del libro de la bibliografía complementaria “Autómatas,
Gramáticas y Lenguajes formales: problemas resueltos”.
Se trata de construir el analizador sintáctico LR(k) para la gramática
independiente del contexto G = ({S}, {x, y, z}, S, P ), donde P es el siguiente
conjunto de producciones:

S → xSz
S → ySz
S→z

6
Dada cualquier cadena perteneciente al lenguaje generado por G, a la hora
de decidir qué producción utilizar para derivarla, basta con inspeccionar si a
continuación viene una x, una y o una z. Por tanto, para esta gramática se necesita
un único símbolo de preanálisis.
Los estados del autómata finito correspondiente a la tabla de análisis de un
analizador sintáctico LR, se corresponden con una determinada derivación de la
gramática. Los estados de aceptación se corresponden con situaciones en las que
el autómata ha reconocido una derivación válida de la gramática.
Por tanto, en cada estado se indica para cada producción de la gramática, la
parte que ya se ha reconocido y la parte que queda por identificar. Así por ejemplo,
si se supone que de la producción S → xSz ya se ha reconocido la x, el autómata
debería poder controlar que resta por reconocer la subcadena que se obtendría
al derivar el no terminal S. Para facilitar la comprensión del funcionamiento
del autómata, se marcará la producción con un ˆ indicando qué posibilidades
de derivación hay en ese estado. Por lo tanto, en este ejemplo, la producción se
representaría de esta forma S → xˆSz.
Para el ejemplo de esta producción, al estado del autómata, se le añadirán
las transiciones correspondientes a las producciones que contengan el símbolo no
terminal S en la parte izquierda, por lo tanto el estado del autómata (que será el
estado D en la representación gráfica del autómata) queda definido por:

S → xˆSz

S → ˆxSz

S → ˆySz

S → ˆz

A continuación se describe el proceso de definición del resto de estados del


autómata.
El estado inicial, marcado con A, contendrá una producción extra que
solamente genera el símbolo inicial de la gramática, por lo tanto el estado queda
definido por las siguientes producciones:

S ′ → ˆS

S → ˆxSz

S → ˆySz

S → ˆz

7
Otro estado, el estado B, indica que se ha reconocido la producción S ′ → S.
Para ello, se representará el estado poniendo el símbolo ˆ detrás de la producción
(S ′ → Sˆ). El estado B es un estado de aceptación (porque es un estado en el que
ya se ha reconocido una producción).
El estado C, es el estado al que se accede cuando en la cinta de entrada hay un
símbolo y y se está reconociendo la producción S → ySz. Este estado se define:

S → yˆSz
S → ˆxSz
S → ˆySz
S → ˆz

Otro estado, el estado D se ha definido al principio del ejercicio como ejemplo.

Estado x y z FDC S
A desplazar D desplazar C desplazar E B
B Aceptar
C desplazar D desplazar C desplazar E F
D desplazar D desplazar C desplazar E H
E S→z S→z
F desplazar G
G S → ySz S → ySz
H desplazar I
I S → xSz S → xSz

Cuadro 1: Tabla de transición del autómata de un analizador LR(1)

El estado E, al que se accederá cuando se reconozca un símbolo z de la


producción S → z, es un estado de aceptación. Este estado de aceptación
representa la situación en la que se ha reconocido la producción S → z y por
tanto, el autómata debe regresar al estado donde se empezó a reconocerla y dar
por reconocido el símbolo no terminal S.
Al estado F se accederá después de reconocer el símbolo no terminal S en
la producción S → ySz y por lo tanto el estado estará definido por S → ySˆz.
Como el único elemento de la producción que queda por identificar es un símbolo
terminal, no se introduce ninguna producción más en este estado.
Al estado G, se llega después de reconocer el símbolo terminal z en la
producción S → ySz, y por tanto es un estado de aceptación que reconoce esta
producción.

8
y

y S z
A C F G
z
z

S E y x
x
z
S z
B D H I

x
Figura 1: Autómata del analizador sintáctico LR(1) del ejercicio

El estado H, es similar al estado F solo que con la producción S → ˆxSz.


Por su parte, el estado I, es equivalente al estado G solo que con la producción
S → ˆxSz.
Para terminar de representar cada estado se utilizará la tabla 1. En esta tabla, se
representa qué acción realizar para cada estado y para cada símbolo de preanálisis.
Los símbolos de preanálisis son los elementos terminales del lenguaje, junto
con la Marca de Final de Cadena (FDC). Cuando en la tabla aparece la acción
“desplazar” el autómata leerá de la cinta de entrada el símbolo actual y lo apilará
en la pila junto con el estado que acompaña a la acción “desplazar”. Además,
se utilizará la pila para ir introduciendo los elementos terminales que se van
leyendo, de forma que se irá constuyendo dentro de la pila la producción que
se está resolviendo. Cuando se ha reconocido íntegramente la parte izquierda de
una producción, se desapilan todos sus elementos y se apila la parte derecha de la
producción. También se introduce en la pila una marca para saber sobre qué estado
se está realizando el trabajo, ya que cada vez que se reconoce una producción,
se debe volver al estado en el cual se ha empezado a reconocer la producción
para continuar con el reconocimiento de la cadena. En la figura 1 se muestra el
diagrama de transiciones del autómata correspondiente al analizador LR(1) del
enunciado.

Ejemplo de ejecución del autómata construido anteriormente


A continuación se va a considerar la cadena de entrada yxyzzzz con el
objetivo de ilustrar el funcionamiento del autómata.
Esta cadena pertenece al lenguaje generado por la gramática del enunciado. En

9
la siguiente tabla se muestra la ejecución del autómata con esta cadena de entrada,
teniendo en cuenta, además, la tabla de transiciones de la tabla 1
En los pasos 5, 8 y 11 se han reconocido derivaciones válidas de la gramática,
razón por la cual la pila se ha vaciado hasta llegar al estado previo al comienzo
del reconocimiento de estas producciones. En estos pasos, se ha leído de la pila
pero no de la entrada. De esta forma, sabiendo en qué estado estaba el autómata y
cuál era el simbolo no terminal de la gramática, el autómata se ha transladado al
estado adecuado sin necesidad de leer ningún elemento de la cinta.

Paso Cinta Estado Pila Tabla de Transición


0 yxyzzzz A desplazar C
1 yxyzzzz C yC desplazar D
2 yxyzzzz D yCxD desplazar C
3 yxyzzzz C yCxDyC desplazar E
4 yxyzzzz E yCxDyCzE S→z
5 yxyzzzz C yCxDyCSF
6 yxyzzzz F yCxDyCSF desplazar G
7 yxyzzzz G yCxDyCSFzG S → ySz
8 yxyzzzz D yCxDSH
9 yxyzzzz H yCxDSH desplazar I
10 yxyzzzz I yCxDSHzI S → xSz
11 yxyzzzz C yCSF
12 yxyzzzz F yCSF desplazar G
13 yxyzzzzλ G yCSFzG S → ySz
14 yxyzzzzλ B SB Aceptar

10
Preguntas
Cada pregunta acertada suma un punto. Las preguntas no contestadas o
erróneas sumarán 0 puntos a la calificación final de la práctica.

1. Indicar cuántos símbolos de preanálisis son necesarios para construir un


analizador LR(k) para la gramática definida a continuación. Justifique
brevemente su respuesta.

S = ({S}, {x, y, z}, S, P )

donde S es el símbolo inicial de la gramática y P es el siguiente conjunto


de producciones:

S → xxSzz
S → yxSzz
S → yySzz
S → xySzz
S →ǫ

2. Indicar cuál de las siguientes afirmaciones es falsa.

a) Es posible construir un analizador LR(k) para cualquier lenguaje


independiente del contexto determinista.
b) Es posible construir un analizador LR(k) para cualquier lenguaje
regular.
c) Existen lenguajes independientes del contexto para los que no es
posible construir un analizador LL(k)
d) Es posible construir un analizador LL(k) para cualquier lenguaje
independiente del contexto.

3. Indicar cuál de las siguientes afirmaciones es verdadera:

a) Es posible construir un analizador LR(k) para cualquier lenguaje


independiente del contexto determinista.
b) Es posible construir un analizador LL(k) para cualquier lenguaje
independiente del contexto.
c) No siempre es posible construir un analizador LR(k) para cualquier
lenguaje regular.

11
d) Ninguna de las anteriores afirmaciones es verdadera.

4. Dada la siguiente gramática: G = ({S, A, B}, {x, y, z}, S, P ) donde S


es el símbolo inicial de la gramática y P es el siguiente conjunto de
producciones:

S → AB
A → xAy
A→ǫ
B → yBz
B → yz

Indicar cuántos símbolos de preanálisis son necesarios. Justificar


brevemente su respuesta.
5. Dado el alfabeto Σ = {c, a, b} y el lenguaje L = {can cb2n c : n ≥ 0},
definir una gramática que genere L.
6. Dada la siguiente gramática G = ({S, A}, {a, b, c}, S, P ) donde S es
el símbolo inicial de la gramática y P es el siguiente conjunto de
producciones:

S → caSbc
S→A
A → aAb
A→b

Indicar cuántos símbolos de preanálisis son necesarios. Justificar


brevemente su respuesta.
7. Dada la gramática del ejercicio anterior, definir el lenguaje L(G). ¿Es
posible definir un analizador sintáctico LR(k) para este lenguaje?.
8. Dado el analizador LR(1) definido en el enunciado de la práctica y teniendo
en cuenta la tabla 1, indicar el funcionamiento del analizador cuando en la
entrada tenga la cadena xxyzzzz.
9. Dada la gramática G = ({S, N}, {x, y, z}, S, P ) donde S es el símbolo
inicial de la gramática y P es el siguiente conjunto de producciones:

S → xyN
N → zS
N →z

12
Indicar cuántos símbolos de preanálisis son necesarios para construir un
analizador sintáctico LR(k). Justificar brevemente su respuesta.

10. Definir la tabla de análisis sintáctico LL(1) para la gramática G =


({S}, {x, y}, S, P ) donde S es el símbolo inicial de la gramática y P es
el siguiente conjunto de producciones:

S → xSz
S → ySz
S→ǫ

Referencias
[1] J.G. Brookshear. Teoría de la Computación: Lenguajes Formales, Autómatas
y Complejidad. Addison-Wesley Iberoamericana, 1993.

13

Vous aimerez peut-être aussi