La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Lenguajes de libre contexto

Presentaciones similares


Presentación del tema: "Lenguajes de libre contexto"— Transcripción de la presentación:

1 Lenguajes de libre contexto

2 Gramáticas Una gramática es otra forma de describir un lenguaje.
Ejemplo: O  S P S  A T P  V A  el A  un T  niño T  perro V  corre V  camina

3 Gramáticas O  S P Una derivación de "un niño corre":
S  A T P  V A  el A  un T  niño T  perro V  corre V  camina Una derivación de "un niño corre": O  S P  A T P  A T V  un T V  un T corre  un niño corre

4 Gramáticas O  S P Lenguaje descrito por esta gramática:
S  A T P  V A  el A  un T  niño T  perro V  corre V  camina Lenguaje descrito por esta gramática: L={"el niño corre", "el niño camina", "un niño corre", "un niño camina", "el perro corre, el perro camina", "un perro corre", "un perro camina"}

5 T  niño Gramáticas Variable o "no-terminal" Regla de producción

6 Gramáticas Otro ejemplo: Lenguaje: Algunas derivaciones:
Este formalismo permite describir algunos lenguajes que no son regulares.

7 Gramática: forma general
Una gramática es una tupla G=(V,T,S,P) donde T es un conjunto finito de símbolos terminales (es el alfabeto en que estarán escritas las palabras). V es un conjunto finito de variables (símbolos que no aparecerán en la palabra final). S  V es la variable de inicio. P es un conjunto finito de reglas de producción de la forma pq, donde p es de la forma (V+T)*V(V+T)* y q es de la forma (V+T)*.

8 Derivaciones T={a,b} V={S} S=S P={SaSb, S}
Una derivación es la obtención de una palabra u a partir de una palabra v, ambas pertenecientes a (V+T)*, aplicando una regla de producción: Si la regla es pq, será aplicable sólo si p está incluida en u, o sea, u=xpy. El resultado será v=xqy. Escribimos uv.

9 Derivaciones Definimos * como la cerradura transitiva de .
Es decir, u * v ssi u1,u2,...,uk tales que u  u1  u2  ...  uk  v Además definimos que u * u para todo u. Definimos el lenguaje descrito por la gramática como el conjunto de todas las palabras de terminales que pueden derivarse a partir de S: L(G) = { wT*: S * w } A veces cuando no haya confusión posible, anotaremos * simplemente como 

10 Convención sobre notación
Notación: se suelen usar en este contexto Letras minúsculas del comienzo del alfabeto para los terminales. Letras minúsculas del final del alfabeto para las palabras (de terminales, o de terminales mezclados con variables). Letras mayúsculas para las variables. Cuando por algún motivo esto pueda inducir a confusión, entonces se usan con corchetes: S. Casos típicos: cuando es inevitable usar mayúsculas en los terminales, o cuando una variable tiene un nombre natural (ejemplo: predicado).

11 Gramáticas de libre contexto
El formalismo general de gramáticas es demasiado poderoso : puede describir lenguajes más complejos que los que nos interesan (por ahora). Estudiaremos en este capítulo las gramáticas de libre contexto y sus lenguajes asociados (lenguajes de libre contexto). Nota: también lo traducen como “de contexto libre”, “independientes del contexto”, etc, etc.

12 Gramáticas de libre contexto
Def.: una gramática se dice de libre contexto (GLC) ssi en toda regla de producción pq, se tiene que pV. Es decir, las reglas son de la forma X  w con w(V+T)*. ¿Por qué interesan?  Los lenguajes de programación, y (salvo algunas construcciones particulares) los lenguajes naturales, son de libre contexto!!

13 (Gramáticas regulares
Un caso aún más particular son las GLC en que se pide que todas las reglas de producción sean de las formas SaT ó Sa Se les llama gramáticas lineales por la derecha. Análogamente se definen las gramáticas lineales por la izquierda, que tienen reglas de la forma STa, ó bien Sa. Una gramática es regular si es lineal por la derecha, o es lineal por la izquierda.

14 Gramáticas regulares L es un lenguaje regular ssi L=L(G) para una gramática regular G. Nota: en la clase el estado C estaba anónimo, y las producciones que incluyen C estaban resumidas en una sola, AaaB. Lo cambio aquí para respetar el formato definido en la transparencia previa. Ir derivando la palabra corresponde a ir cambiando de estado interno (la variable), y escribiendo.

15 Gramáticas regulares)
Ojo: para que G sea regular debe ser lineal por la derecha o por la izquierda, pero no puede mezclar las dos formas. Si las mezcla, puede que lo que sale ya no sea regular: SaT TSb S genera {anbn, n0}, que es el clásico ejemplo de lenguaje no regular.

16 GLC, ejemplos Volvamos al caso general de GLC. Otro ejemplo:
¿Lenguaje?  {anbn+1, n0} Una notación conveniente: cuando el lado izquierdo es el mismo, agrupamos varias reglas de producción en una sola línea mediante “|”:

17 GLC, ejemplos Más ejemplos: donde T={(,)}, V={S}. ¿Lenguaje?
S  (S)  () S  (S)  (SS)  ((S)S)  ((S)(S))  (()(S))  (()()) da el lenguaje de los paréntesis bien balanceados.

18 GLC, ejemplos Más ejemplos: donde T={a,b}, V={S}. ¿Lenguaje?
S  aSa  abSba  abbSbba  abbbba  es el lenguaje de los palíndromes de largo par: L={ w=u uR, u{a,b}*} Ejercicio: modificar la gramática para que también genere palíndromes de largo impar (p.ej., aba).

19 GLC, ejercicios Ejercicios:
¿Qué lenguajes describen las siguientes gramáticas? G1: S  XaaX X  aX | bX |  G2: S  XY X  aX | bX | a Y  Ya | Yb | a Escriba una gramática que describa el lenguaje de todas las expresiones regulares válidas sobre el alfabeto {a,b}.

20 GLC "prototípicas" Algunas formas típicas de LLC (lenguajes de libre contexto): Recursivos Por partes Anidados Algunos ejemplos para tener en cuenta (como “principios de diseño”): Recursivo: {anbn, n0} Lo generamos con algo de la forma SaSb El de los palíndromes es análogo. Idea: hay un "surtidor" al medio que emite letras de manera simétrica.

21 GLC "prototípicas" Por partes: L={anbnambm, n0, m0}
Lo generamos con algo de la forma SXY Luego a partir de X e Y generamos las dos partes (aprovechando que no tienen relación). En este caso L=L1L1, con L1={anbn, n0}, de modo que X e Y pueden ser el mismo: SXX XaXb | 

22 GLC "prototípicas" Anidados: L={anbmambn, n0, m0}
Aquí las “partes independientes” son por un lado anbn y por otro lado bmam, que está dentro de la anterior. Generamos primero lo exterior, luego lo interior. En este caso particular, tanto lo interior como lo exterior es del tipo recursivo. SaSb | X XbXa | 

23 GLC, un ejemplo más complejo
L={w{0,1}*: w tiene dos bloques de 0’s del mismo tamaño}. Permitidos: 01011, , No permitidos: , 01111 D Cantidad de 0’s: la misma a cada lado al menos uno inicio parte central final A B C A: , ó termina en 1 C: , ó comienza con 1

24 GLC, un ejemplo más complejo
 De modo que descomponemos por partes, y luego aplicamos recursividad en B. inicio parte central final A B C D Cantidad de 0’s: la misma a cada lado al menos uno A →  | U1 U → 0U | 1U |  C →  | 1U D → 1U1 | 1 S → ABC B → 0B0 | 0D0

25 Derivaciones y árboles
Cuando una derivación pasa por algún punto en que hay más de una variable, significa que habrá varias derivaciones equivalentes, según cuál sea el orden en que aplicamos las producciones: S XY aaXY aaXYb aaaaXYb aaaaXb aaaab S XY aaXY aaaaXY aaaaY aaaaYb aaaab

26 Derivaciones y árboles
Dentro de esta variedad de derivaciones equivalentes, distinguimos la derivación izquierda y la derivación derecha (anotadas por L y R respectivamente). S XY aaXY aaaaXY aaaaY aaaaYb aaaab es una derivación izquierda: en cada paso reemplazamos la variable que está más a la izquierda. Una derivación derecha sería: S XY XYb Xb aaXb aaaaXb aaaab

27 Derivaciones y árboles
Árbol de análisis sintáctico (o “árbol de derivación”): representa la derivación, sin importar el orden: SL aaaab : S XY aaXY aaaaXY aaaaY aaaaYb aaaab SR aaaab : S XY XYb Xb aaXb aaaaXb aaaab

28 Derivaciones y árboles
En la raíz va S. En las hojas, terminales o . Las derivaciones extremas, L y R, corresponden a hacer recorridos del árbol en pre-orden y post-orden, respectivamente. Nótese que en este caso hay más de una derivación, pero el árbol es único (no hay otra forma de derivar aaaab). No siempre será el caso.

29 GLC: ambigüedad x + y + z E  E + E | (E) | V V  x | y | z E + V x y
E  E+E  V+E  x+E  x+E+E  x+V+E  x+y+E  x+y+V  x+y+z E  E+E  E+E+E  V+E+E  x+E+E  x+V+E  x+y+E  x+y+V  x+y+z Aquí hay dos árboles distintos, cada uno con una derivación izquierda distinta.

30 GLC: ambigüedad Analizador léxico T o t a l = p r e c i + v ; Total = precio + iva ; asignación Parser Total := Expresión id id precio iva Los parseadores construyen árboles de análisis sintáctico. El árbol indica cómo se entiende el texto.

31 GLC: ambigüedad x  y + z E  E + E | EE | (E) | V V  x | y | z  
La existencia de más de un árbol de derivación puede ser entonces nefasta: E  E + E | EE | (E) | V V  x | y | z E + V x y z E + V x y z x  y + z “Primero y+z, luego x  eso” “Primero xy, luego eso + z”

32 GLC: ambigüedad Un caso clásico de ambigüedad en lenguajes de programación: dos ifs, un else. La mayoría de los lenguajes lo resuelven asignando el else al if más cercano. S if b then else a if b then S

33 GLC: ambigüedad Decimos que una gramática G es ambigua, si existe una palabra en L(G) que admite más de un árbol de derivación. Nota: puede haber más de una derivación sin que indique ambigüedad, siempre y cuando sigan el mismo árbol. Otro motivo que hace nefasta la ambigüedad: para el parser es más fácil encontrar un árbol de derivación si la solución es única.

34 GLC: ambigüedad E  E + E | EE | (E) | V V  x | y | z
La buena noticia: a veces podemos cambiar la gramática por otra equivalente (i.e., mismo lenguaje) pero sin ambigüedad: E  E + E | EE | (E) | V V  x | y | z Genera lo mismo, pero obliga al árbol a reconocer la prioridad de la multiplicación. Java o C++ aplican algo análogo para resolver los else ambiguos. E  T | E + T T  F | T  F F  (E) | V V  x | y | z

35 GLC: ambigüedad Las malas noticias:
A veces un lenguaje es inherentemente ambiguo: sólo existen gramáticas ambiguas que lo describen. Si tenemos una gramática ambigua, no existe un algoritmo general que nos diga acaso es intrínsecamente ambigua. Y aún si no lo fuera, tampoco hay método infalible para “desambiguarla”.

36 GLC: ambigüedad Ejemplo de lenguaje inherentemente ambiguo (sin demostrar): Una palabra de la forma anbncn tiene dos árboles distintos. Lo que no demostraremos es que para cualquier otra gramática equivalente, pasa lo mismo. Ver Hopcroft.

37 GLC: ambigüedad Ergo: Se puede tratar de evitar la ambigüedad.
A veces hay que convivir con ella. Un lenguaje de programación debiera diseñarse con una gramática que evite la ambigüedad (tanto para evitar errores de interpretación, como para facilitar el parseo).

38 GLC: simplificación Dada una gramática G, digamos, S → aSb | bSaSb | T
T → S |  si además nos dan un string w, ¿Cómo sabemos acaso wL(G)? En caso de que esté, ¿cómo obtenemos un árbol de derivación? Y en caso de obtenerlo, ¿es único?

39 GLC: simplificación w=aabbb ¿está?               
S → aSb | bSaSb | T T → S |  w=aabbb ¿está? Por fuerza bruta: podemos intentar generar todas las posibles derivaciones, buscando alguna que genere w. S aSb aaSbb abSaSbb ... Problema: ¿Cuándo parar? aTb bSaSb baSbaSb ... T S ...

40 GLC: simplificación w=aabbb ¿está? S  aSb  abSaSbb  abSabb  ababb
S → aSb | bSaSb | T T → S |  w=aabbb ¿está? Una idea: parar cuando la palabra que tenemos exceda la longitud de w. Problema #1: si existen reglas de producción que llevan a , entonces la longitud no necesariamente va aumentando. S  aSb  abSaSbb  abSabb  ababb Para evitar esos "acortamientos", sería bueno que no hubiera producciones nulas como esa.

41 GLC: simplificación w=aabbb ¿está? S  aSb  aTb  aSb  …
S → aSb | bSaSb | T T → S |  w=aabbb ¿está? Una idea: parar cuando la palabra que tenemos exceda la longitud de w. Problema #2: podemos quedarnos pegados en loops, si se dan casos en que, por ejemplo, S→T, T→S. S  aSb  aTb  aSb  … Así que sería bueno evitar situaciones de ese tipo también.

42 Eliminación de producciones nulas
Decimos que una variable X es anulable si existe una derivación X *  Para determinar las variables anulables, aplicamos un algoritmo de marcado recursivo: Marcamos todas las variables X que tengan una regla de producción X. Mientras exista una regla de producción de la forma YX1...Xm donde Y no está marcado pero todos los Xi lo están, marcar Y.

43 Eliminación de producciones nulas
Para eliminar las producciones nulas, (1) Determinar todas las variables anulables, X1,...,Xk (2) Para cada producción de la forma YuXiv, agregar una producción Yuv. Más en general: si el lado derecho incluye más de una variable anulable, considerar cada combinación de anulación. Por ejemplo, si YuXabYv, y tanto X como Y son anulables, agregamos YuabYv YuXabv Yuabv

44 Eliminación de producciones nulas
Para eliminar las producciones nulas, (3) Si Xi es una producción, eliminarla. (4) Si S es anulable, entonces agregar una producción S (salvo que ya exista). Esa última salvedad es importante: Si  no está en el lenguaje de la gramática, entonces S no es anulable y no habrá producciones nulas. Si  está en el lenguaje de la gramática, entonces S es anulable y la única producción nula será S.

45 Eliminación de producciones nulas
Ejemplo: S  a | Xb | aYa X  Y |  Y  b | X Anulables: X e Y. Eliminamos X   A partir de SXb se agrega Sb, pues X es anulable. A partir de SaYa se agrega Saa, pues Y es anulable, S  a | Xb | aYa | b | aa X  Y Y  b | X

46 Eliminación de producciones nulas
Otro ejemplo: Gramática inicial Gramática final Sustituimos Anulables: M

47 Eliminación de producciones unitarias
Una "producción unitaria" es de la forma XY; decimos que existe una derivación unitaria entre dos variables X e Y si se tiene X * Y. Si no hay producciones nulas (y supondremos aquí que ya las eliminamos), entonces X * Y sólo es posible mediante una cadena de producciones unitarias. Producción S X - Y X  S Y  X Y  X  S S  aX | Yb X  S Y  bY | b | X

48 Eliminación de producciones unitarias
Para eliminar derivaciones unitarias: Para cada par de variables tales que X * Y, introducimos nuevas producciones: para cada producción no-unitaria de Y, Ys1, Ys2, ..., agregamos Xs1, Xs2, ... Se hace simultaneamente para todos los pares X,Y con derivación unitaria. Después se eliminan las producciones unitarias, y todo lo redundante (que suele aparecer).

49 Eliminación de producciones unitarias
Ejemplo: S  aX | Yb X  S Y  bY | b | X Como X * S, se agregan XaX , XYb Como Y * X... No se agrega nada. Como Y * S, se agregan YaX , YYb Finalmente, eliminamos las producciones unitarias XS y YX. La nueva gramática quedaría S  aX | Yb X  aX | Yb Y  bY | b | aX | Yb

50 Eliminación de variables inútiles
Al ir transformando las gramáticas (manteniendo, recordemos, el mismo lenguaje) pueden aparecer variables o producciones inútiles. Dos formas típicas son: Y es inútil: no hay forma de que aparezca en una derivación! X es inútil: nunca desaparece, así que no puede formar parte de la derivación de una palabra del lenguaje.

51 Eliminación de variables inútiles
En general una variable será útil cuando exista una palabra wL(G) en cuya derivación aparezca: Si esto nunca ocurre, es inútil. Una producción es útil sólo si todas sus variables son útiles. De lo contrario, es inútil. Para determinar las variables inútiles: son aquellas que no producen strings de terminales, o bien, que no son accesibles desde S.

52 Eliminación de variables inútiles
Para encontrar las variables que producen strings de terminales, definimos un conjunto U=T. Luego iteramos: Si existe una variable XU, pero que tiene una producción Xu, uU*, entonces agregamos X a U. Si no existe ninguna variable así, salimos de la iteración. U={a,b} U={a,b,A} U={a,b,A,S} U={a,b,A,S,B}

53 Eliminación de variables inútiles
Las variables que hayan quedado fuera de U son las que no producen strings de terminales, ergo, son inútiles. Las eliminamos, así como todas las producciones en que aparezcan. U={a,b} U={a,b,A} U={a,b,A,S} U={a,b,A,S,B}

54 Eliminación de variables inútiles
A continuación eliminamos las variables que no se alcancen desde S. Eso es aplicar recorrido de grafos, de EDA: Generamos grafo de dependencia B no es alcanzable  Es inútil.

55 Eliminación de variables inútiles
Simplificación de gramáticas: Eliminar producciones nulas Eliminar producciones unitarias Eliminar variables inútiles (1) y (2) son relevantes para que las cosas que siguen a continuación funcionen. (3) es útil para no acarrear lastre, que suele aparecer como subproducto de (1) y (2).

56 Aumento de longitud en la derivación
Sea G una gramática sin producciones nulas ni unitarias. Entonces, en una derivación, cada paso aumenta la longitud de la palabra, o bien lo mantiene constante pero a costa de reducir la # de variables. ¿Motivo? Cada paso de la derivación reemplaza una variable X por algo, pero ese "algo" no es  ni tampoco es una variable "desnuda". Alternativas: Reemplazar por una expresión con más de un símbolo: la expresión se alarga. Reemplazar por un símbolo terminal: queda con la misma longitud, pero con una variable menos.

57 Aumento de longitud en la derivación
En ese caso sí podemos aplicar la fuerza bruta para evaluar acaso un string w pertenece a una gramática G: Probamos todas las derivaciones de largo a lo más |w| (es decir, todas las combinaciones de a lo más |w| reglas de producción). Esa es ahora una cantidad finita, así que podemos probarlas en tiempo finito. En el peor de los casos, probamos |P|+|P|2+...+|P||w| combinaciones. wL(G) ssi alguna de esas derivaciones la genera.

58 La buena y mala noticia Es buena noticia: nos da un algoritmo para saber si una palabra está en el lenguaje. La mala noticia: es pésimo. La cantidad de casos crece exponencialmente en la medida que crece |w|. Para un código Java de 200 líneas, el tiempo de ejecución sería astronómico ( 10200). Existe un algoritmo más eficiente, pero requiere transformar la gramática un poco más:

59 Forma Normal de Chomsky (FNC)
Una gramática está en la forma normal de Chomsky (FNC) si cada producción (a excepción de S, si que existe) es de una de las dos siguientes formas: XYZ o bien Xa donde, como de costumbre, X,Y,ZV, aT. Está en FNC No está en FNC

60 Forma Normal de Chomsky (FNC)
Teorema: Para toda gramática de libre contexto G, existe una gramática de libre contexto G' en forma normal de Chomsky que es equivalente a G (es decir, L(G)=L(G')). Para demostrarlo, tenemos que ver que podemos transformar cualquier GLC hasta que quede en FNC. 1. Eliminamos las producciones nulas, unitarias, e inútiles. 2. Eliminamos los lados derechos "mixtos":

61 Forma Normal de Chomsky (FNC)
Para eliminar los lados derechos mixtos, creamos una nueva variable T por cada terminal . Reemplazamos cada  por el T respectivo, y agregamos un producción T. Ojo: si alguna producción ya era de la forma X, la dejamos así (no la cambiamos a XT). Así evitamos introducir producciones unitarias.

62 Forma Normal de Chomsky (FNC)
3. Reemplazamos toda producción de la forma AC1C2...Cn por una cadena de producciones AC1V1, V1C2V2, ... , Vn-2Cn-1 Cn donde los V1,...,Vn-2 son nuevas variables, intermedias.

63 Forma Normal de Chomsky (FNC)
Y listo. Llevar GLC a la forma normal de Chomsky es relativamente fácil. Tener la GLC en FNC sirve para varias cosas, prácticas y teóricas. La más importante: para parsear en tiempo polinomial en |w|.

64 CYK Algoritmo CYK (Cocke-Younger-Kasami):
Input: una GLC G en FNC, y una palabra w. Output: acaso wL(G) [y fácilmente se puede pedir que además dé un árbol de derivación, en caso de respuesta positiva] Idea: determino las variables que producen todas las subpalabras de w de largo 1. Luego las que producen todas las subpalabras de w de largo 2. Etc...

65 CYK Para una palabra de largo k dada (digamos, u), consideramos las posibles formas de descomponerla en 2 palabras más cortas. Si para una descomposición u=v1v2 se tiene que X * v1, Y * v2 (esa información ya está en la tabla), y existe ZXY (eso lo miro en la gramática), entonces Z*u. Al terminar: wL(G) ssi S es una de las variables que producen w.

66 CYK Ejemplo, con gramática:
Subpalabras de largo k, partiendo de posición j k \ j 1 2 3 4 5 y palabra

67 CYK Variables que generan las subpalabras de largo 1

68 CYK Variables que generan las subpalabras de largo 2

69 CYK Variables que generan las subpalabras de largo 5 (o sea, w)

70 CYK Es fácil modificar CYK para que me entregue un árbol de derivación: cada vez que ponemos una variable en la tabla, debemos recordar por qué la pusimos. Luego con esa información recuperamos el árbol. CYK llena una tabla de tamaño  |w|2. Para cada item de la tabla hay que hacer algo de trabajo. Se puede demostrar que el tiempo de ejecución es  |w|3. Mucho mejor que |P||w|.

71 PDA (Autómatas de pila)
Antes teníamos la correspondencia lenguajes regulares  autómatas finitos. También para los LLC existe, no sólo un tipo de gramática, sino también un tipo de máquina que los reconoce. Como los lenguajes regulares son un subconjunto propio de los LLC, el nuevo tipo de máquina es una generalización del que teníamos antes.

72 PDA (Autómatas de pila)
Input Pila Estados PDA Al AF le agregamos una pila (stack). Memoria potencialmente infinita, pero de acceso restringido.

73 PDA (Autómatas de pila)
El resultado es un autómata de pila, autómata apilador, o en inglés pushdown automaton (PDA). A medida que el PDA lee su input, puede sacar o guardar símbolos en la pila. Sus cambios de estados, y lo que haga con la pila, dependerán de lo que va leyendo en el input y de lo que saque de la pila. PDA

74 PDA (Autómatas de pila)
No determinismo: Los PDA que veremos, salvo que se indique lo contrario, son no deterministas : hay transiciones , puede haber más de una transición para una misma situación, etc... Lenguaje reconocido: El lenguaje reconocido por el PDA será el conjunto de palabras para las cuales existe una secuencia de transiciones que concluye con la palabra leída completa, y en estado de aceptación. Es decir, igual que en AFND+.

75 PDA (Autómatas de pila)
La pila tiene su propio alfabeto,  (que puede coincidir en parte con el del input). Existe un símbolo especial $   que señala "el fondo" de la pila (algunos libros usan z0, u otro). Al comienzo es lo único que la pila contiene. leo del input saco de la pila guardo en En el grafo de transiciones se anota lo que se saca y guarda en la pila.

76 input pila tope Reemplaza

77 input pila tope Guarda ("push")

78 input pila tope Saca ("pop")

79 input pila tope Sin cambio

80 PDA (Autómatas de pila)
No determinismo Transición 

81 PDA (Autómatas de pila)
Formalmente, un PDA es una tupla M = ( Q,  , q0, F ) Q es un conjunto finito de estados  es el alfabeto de entrada  es el alfabeto de la pila q0 es el estado inicial FQ son los estados de aceptación  es la función de transición, : Q({})({})  2Q({})

82 PDA, ejemplo Recordatorio:  también se escribe a veces como “”, o también como “”. En los monos que siguen, está como  porque eran muchos para cambiarlos! Ejemplo:

83 PDA, ejemplo Input Pila estado actual

84 PDA, ejemplo Input Pila

85 PDA, ejemplo Input Pila

86 PDA, ejemplo Input Pila

87 PDA, ejemplo Input Pila

88 PDA, ejemplo Input Pila

89 PDA, ejemplo Input Pila

90 PDA, ejemplo Input Pila

91 PDA, ejemplo Input Pila acepta

92 PDA, ejemplo En general, el lenguaje que este PDA acepta es

93 PDA, otro ejemplo Otro ejemplo, ahora con lenguaje:

94 PDA, otro ejemplo Input Pila

95 PDA, otro ejemplo Input Pila

96 PDA, otro ejemplo Input Pila

97 PDA, otro ejemplo Input Pila

98 PDA, otro ejemplo Input Pila

99 PDA, otro ejemplo Input Pila

100 PDA, otro ejemplo Input Pila acepta

101 Un ejemplo más: Vimos que la gramática de abajo genera el lenguaje L={w{0,1}*: w tiene 2 bloques de 0’s del mismo tamaño} Ejemplos de strings en L: 01011, , Ejemplos de strings fuera de L: , 01111 A B C A, parte inicial: , o termina en 1 C, parte final: , o comienza con 1 D B, parte central: zona D, rodeada por bloques de 1 o más 0’s. D, centro del centro: parte y termina con 1. [transparencia 23] A →  | U1 U → 0U | 1U |  C →  | 1U D → 1U1 | 1 S → ABC B → 0B0 | 0D0

102 Un ejemplo más: He aquí un PDA que reconoce ese mismo lenguaje: B A D
e, e / e 1, e / e 0, e / 0 q0 q1 q2 q3 1, e / e e, e / e 0, e / e C 0, e / e 1, e / e q4 1, e / e 1, e / e 1, e / e 1, $ / $ q6 q5 0, 0 / e q7 e, $ / $

103 Guardando strings String guardado
Permitiremos ahora guardar strings en la pila: tope pila Guarda “cda” string guardado

104 Guardando strings El poder de cómputo no cambia, pues
es equivalente a tener

105 Y otro ejemplo...

106 Input Pila

107 Input Pila

108 Input Pila

109 Input Pila

110 Input Pila

111 Input Pila

112 Input Pila

113 Input Pila acepta

114 Idea: este PDA guarda en la pila sólo lo que está en exceso, respecto a na=nb.
Se usó esta vez 0 y 1 en la pila, para representar a y b respectivamente. Esto es frecuente: distinguir los alfabetos ayuda a no confundirse.

115 PDA: descripción instantánea
Tiempo atrás definimos la configuración o descripción instantánea de un AF (la “foto” del AF en un momento dado) como (q,w), donde q era el estado actual y w era la parte del input que faltaba por leer. Es similar para PDA, pero ahora la “foto” debe incluir el contenido de la pila. (q,w,v) q  Q, el estado actual v  , el contenido actual del stack w  , lo que falta por leer en el input

116 PDA: descripción instantánea
Input Instante 4: Pila

117 PDA: descripción instantánea
Input Instante 5: Pila

118 PDA: descripción instantánea
Escribimos (q1,bbb,aaa$)(q2,bb,aa$) y el cómputo completo será (q0,aaabbb,$) (q1,aaabbb,$) (q1,aabbb,a$) (q1,abbb,aa$) (q1,bbb,aaa$) (q2,bb,aa$) (q2,b,a$) (q2,,$) (q3,,$) Abreviando podemos decir que (q0,aaabbb,$)  (q3,,$) A veces se pone * para especificar que se refiere a más de un paso.

119 Lenguaje por estado de aceptación
Podemos entonces definir formalmente el lenguaje aceptado por un PDA M=(Q,  , q0, F) como L(M) = { w*: qfF,v*, (q0,w,$)(qf,,v) } Recuérdese que los PDA son no-deterministas, así que para que wL(M) basta que exista alguna cadena de cómputos que sirva.

120 Lenguaje por pila vacía
Otra forma de asociar un lenguaje a un PDA es definir N(M) = { w*: qQ, (q0,w,$)(q,,$) } Es decir: el “estado de aceptación” es cualquier estado, pero con la pila vacía, y la palabra de entrada completamente leída. Se demuestra (no es difícil) que las nociones son equivalentes: si L=L(M1) para un PDA M1, existe un PDA M2 con L=N(M2), y viceversa:

121 Lenguaje por pila vacía
Si tenemos un PDA que reconoce por estado de aceptación, y queremos uno que reconozca por pila vacía, hacemos que desde los estados de aceptación pase (con ) a un estado de "limpieza" de la pila. $ $ Ese z0 es un "falso piso", de modo que la pila sólo estará de verdad vacía al llegar a p.

122 Lenguaje por pila vacía
Al revés, si tenemos uno que reconoce por pila vacía, y queremos uno que reconozca por estado de aceptación: desde todos los estados ponemos una transición  y se vaya a un estado de aceptación. , $/ , $/ , $/Z0$ , $/ , $/

123 PDA y GLC Teorema: L es reconocido por algún PDA ssi L es un lenguaje de libre contexto. Demostración, sólo la idea: Hacia un lado es fácil: veamos que si G es una gramática de libre contexto, entonces existe un PDA M con L(M)=L(G). Primero, recordemos como es una derivación izquierda:

124 GLC  PDA Consideremos una derivación izquierda de aabbcbaa con la gramática G. G: S  aSa | YZ Y  bY |  Z  cZ | Y S aSa aaSaa aaYZaa aabYZaa aabbYZaa aabbZaa aabbcZaa aabbcYaa aabbcbYaa aabbcbaa Nótese que: Lo que va quedando a la izquierda se “congela”, y no se vuelve a mirar. Lo que está a la derecha es una pila! S a aa Y Zaa aab aabb Z aabbc aabbcb

125 GLC  PDA Sea G=(V,T,S,P) una gramática de libre contexto.
Construimos el PDA M=(Q,  , q0, F) con Q={q0, q1, q2} F={q2} =T =VT{$}  se construye como sigue: Para cada aT Para cada producción pq en P

126 GLC  PDA Idea: Lo que está en la pila es lo que falta “procesar”.
Si vemos ahí un terminal, significa que lo generó nuestro último reemplazo. Por lo tanto, tiene que tenerlo el input. Si es una variable, la tenemos que reemplazar por alguna de sus producciones.

127 GLC  PDA S aSa aYZa abYZa abZa abcZa abcYa Gramática: S  aSa | YZ
(q0,abcba,$)  (q1,abcba,S$)  (q1,abcba,aSa$)  (q1,bcba,Sa$)  (q1,bcba,YZa$)  (q1,bcba,bYZa$)  (q1,cba,YZa$)  (q1,cba,Za$)  (q1,cba,cZa$)  (q1,ba,Za$)  (q1,ba,Ya$)  (q1,ba,bYa$)  (q1,a,Ya$)  (q1,a,a$)  (q1,,$)  (q2,,$) Gramática: S  aSa | YZ Y  bY |  Z  cZ | Y Ejemplo: derivación izquierda (a la idem), y cómputo en el PDA (a la derecha). S aSa aYZa abYZa abZa abcZa abcYa abcbYa abcba

128 PDA  GLC En dirección contraria es más complicado.
No lo veremos en detalle. Cada libro da una versión un poco distinta. Todas son variaciones en torno a lo que sigue; para dudas, ver el Hopcroft en inglés (v. 2). Consideremos un PDA M=(Q,  , q0, F) en la forma en que los definimos al comienzo (escribiendo símbolos de a uno en la pila, no strings).

129 PDA  GLC Las transiciones entonces son de la forma
donde la a, la X y/o la Y pueden eventualmente ser . Usaremos aquí mayúsculas para los elementos de , para distinguirlos de los terminales en la gramática. Supondremos que el PDA acepta por pila vacía. Queremos una gramática que genere el mismo lenguaje.

130 PDA  GLC En la gramática que se define, los terminales serán  (el alfabeto de entrada del PDA). El símbolo de inicio, S, será un símbolo especial. El resto de las variables corresponderán a los elementos de Q({})Q; los escribiremos de la forma [pXq].  V = {S}  Q({})Q

131 PDA  GLC Las reglas de producción se definen con la intención de que [pXq] produzca exactamente el conjunto de palabras que, leídas en el PDA, pueden llevarnos desde el estado p hasta el estado q, sacando una X de la pila (y sin meternos más abajo): [pXq] * w  (p, w, X) * (q, , )

132 PDA  GLC gramática pda Primero se define S[q0Z0p] para todo pQ.
[pXq] * w  (p, w, X) * (q, , ) Primero se define S[q0Z0p] para todo pQ. Ese Z0 es el "falso piso": al sacarlo, se vacía la pila, y podemos aceptar (si el input se acabó). Si los [q0Z0p] cumplen lo de arriba, estamos bien.

133 PDA  GLC A continuación, para cada transición como la de arriba, se agregan las reglas de producción [pXr]  a [qYr] para todo rQ Idea: una forma de ir de p a r sacando una X de la pila, es leer una a del input, y luego ir de q a r de alguna forma que saque Y de la pila. Nota: Si a=, tendremos [pXr]  a [qYr].

134 PDA  GLC Para hacer la demostración, se demuestra
[pXq] * w  (p, w, X) * (q, , ) Cada implicación se demuestra por inducción; ver Hopcroft en caso de incredulidad. Esta construcción no se usa mucho; la otra tampoco. Sirven básicamente para convencerse de la equivalencia PDAGLC.

135 PDA y determinismo Un DPDA es un deterministic pushdown automaton: lo mismo que un PDA, pero sin ambigüedad en ningún paso. No lo demostraremos formalmente, pero se puede demostrar lo siguiente: si anotamos por LM la clase de lenguajes reconocidos por cada tipo de autómata, tenemos que LAFND+ = LAFD  LDPDA  LPDA y las inclusiones son propias. Así que acá el no-determinismo sí cambia los lenguajes.

136 PDA y determinismo Un ejemplo de lenguaje que está en LDPDA pero no está en LAFD es {anbn:n0} La primera b "me avisa" que tengo que cambiar de estado. Gracias a eso es determinista.

137 PDA y determinismo Un ejemplo de lenguaje que está en LPDA pero no está en LDPDA es {w=u uR : u{a,b}* } Idea: Para reconocer 0n10n necesito que el segundo grupo de 0's "mate" al primero en el stack ...pero entonces ya no podría reconocer 0n10n0n10n, que también está en L.

138 Lema de bombeo para LLC Ahora veamos ejemplos de lenguajes que no están en LPDA; es decir, que no son de libre contexto. Para eso, la herramienta de demostración será el: Lema de bombeo para LLC: sea L un lenguaje de libre contexto. Entonces existe un nN tal que si wL, |w|>N, entonces w puede descomponerse como w = uvxyz de modo tal que |vxy|  n, |vy| > 0 uvkxykz k0

139 Lema de bombeo para LLC w = uvxyz  |vxy|  n, |vy| > 0, uvkxykz k0 Idea: En la derivación, alguna variable R debe aparecer en la producción a partir de si misma. Eso permite bombear: en lugar de producir Rx la segunda vez que aparece, volvemos a hacer Rvxy, y así tantas veces como queramos.

140 Lema de bombeo para LLC

141 Lema de bombeo para LLC La idea, más en detalle:
Si L es LLC, existe una gramática G=(V,T,S,P) en forma normal de Chomsky que lo genera. Si el árbol de derivación de una palabra w es de altura h, entonces |w|2h (pues en el caso extremo, voy reemplazando cada símbolo por 2). Sea n=2|V|+2. Si |w|n, entonces su árbol de derivación es de altura al menos |V|+2. Consideremos un camino de longitud máxima desde S hasta una hoja (terminal).

142 Lema de bombeo para LLC Tiene que haber al menos alguna variable repetida a lo largo de ese camino. Escogemos R como la última repetición que vemos, al ir de arriba hacia abajo. uvkxykz k0 sale de lo anterior, y del mono. |vy| > 0, pues de lo contrario habría producciones unitarias (y no hay, pues estamos en FNC). |vxy|  n, pues de lo contrario R no era la última repetición (se aplicaría a |vxy| el argumento inicial). "QED"

143 Lema de bombeo para LLC, ejemplo
Ejemplo: L={anbncn: n0} no es de libre contexto. Supongamos que sí lo es, y sea m la constante de bombeo. Tomemos w=ambmcm. w tendría una descomposición w = uvxyz con |vxy|  m, |vy| > 0, uvkxykz k0. Nótese que wL, |w|a=|w|b=|w|c.

144 Lema de bombeo para LLC, ejemplo
Ejemplo: L={anbncn: n0} no es de libre contexto. Para que uv2xy2z esté en L, vy debe cumplir también |vy|a=|vy|b=|vy|c Pero como |vxy|  m, vy no puede incluir letras de los tres tipos!  ¡Contradicción! Ejercicio: Usar bombeo para demostrar que el lenguaje {w=u u : u{a,b}* } no es de libre contexto.

145 Lema de bombeo para LLC Warning:
Las demostraciones por bombeo en LLC suelen ser bastante más complicadas que para lenguajes regulares (el ejemplo aquí fue excepcionalmente corto). Nótese que no sabemos nada sobre el u en w=uvxyz. Por lo tanto, a diferencia del bombeo en lenguajes regulares, no podemos asegurar que lo que bombeamos esté al comienzo. Eso obliga, en general, a considerar hartos casos (de posibles ubicaciones de vxy dentro de w).

146 Propiedades de clausura
Sean L1 y L2 dos lenguajes de libre contexto, descritos por las gramáticas G1=(V1,T1,S1,P1) y G2=(V2,T2,S2,P2). Entonces: L1  L2 es de libre contexto En efecto, lo genera la gramática G=(V,T,S,P) con V = V1  V2  {S} T = T1  T2 S una variable nueva P = P1  P2  {S S1|S2}

147 Propiedades de clausura
De manera similar, L1L2 es de libre contexto Tomamos G=(V,T,S,P) con V = V1  V2  {S} T = T1  T2 S una variable nueva P = P1  P2  {S S1S2}

148 Propiedades de clausura
También se tiene que: L1* es de libre contexto Tomamos G=(V,T,S,P) con V = V1  {S} T = T1 S una variable nueva P = P1  {S S S1 | }

149 Propiedades de clausura
Por otro lado, L1  L2 no necesariamente es de libre contexto Contraejemplo: L1={anbncm, n0, m0} y L2={anbmcm, n0, m0} son de libre contexto [ejercicio!], pero L1  L2 = {anbncn: n0} no lo es.

150 Propiedades de clausura
También se tiene que L1C no necesariamente es de libre contexto. Demostración: Los LLC son cerrados bajo la unión. Si además fueran cerrados para el complemento, entonces serían cerrados para la intersección (por leyes de Morgan!). Y acabamos de ver que no lo son.

151 Propiedades de clausura
Lo que sí se cumple es que para L3 regular,  L1  L3 es de libre contexto Idea de la demostración: Consideramos M=(Q,  , q0, F) un PDA que reconoce L1, y M'=(Q', ', q'0, F') un AFD que reconoce L3. Construimos un PDA con estados QQ', que simulará simultáneamente M y M' sobre un mismo input. Aceptamos la palabra si al final tanto M como M' aceptan.

152 Propiedades de clausura
Lo que sí se cumple es que para L3 regular, L1  L3 es de libre contexto Aplicaciones de eso: 1) Probemos que L={anbn: n100} es de libre contexto. Notamos que L = L1  L3, donde L1 = {anbn} L3 = {a,b}* \ {a100b100} Sabemos que L1 es un LLC; por otro lado, L3 es regular.  L es regular.

153 Propiedades de clausura
Lo que sí se cumple es que para L3 regular, L1  L3 es de libre contexto Aplicaciones de eso: 2) Probemos que L={w{a,b,c}*: |w|a=|w|b=|w|c} no es de libre contexto. Por contradicción: si lo fuera, al intersectarlo con a*b*c* (que es regular!) obtendríamos un LLC. Pero L  a*b*c* = {anbncn}, que no es LLC.

154 Problemas de decisión asociados a LLC
Problema de membresía: Dada una gramática de libre contexto G y una palabra w, ¿wL(G)? Respuesta: Algoritmo de parseo exhaustivo (requiere haber eliminado producciones nulas y unitarias). Algoritmo CYK (requiere FNC).

155 Problemas de decisión asociados a LLC
Problema de vacuidad: Dada una gramática de libre contexto G, ¿L(G)=? Respuesta: Aplicar la eliminación de variables inútiles. Ver acaso S es una variable inútil.

156 Problemas de decisión asociados a LLC
Problema de finitud: Dada una gramática de libre contexto G, ¿es L(G) infinito? Respuesta: Eliminar variables inútiles, producciones nulas, producciones unitarias. Hacer un grafo de dependencia entre las variables. L(G) es infinito ssi existe algún ciclo en ese grafo.

157 Problemas de decisión asociados a LLC
Problema de finitud:

158 Problemas de decisión asociados a LLC
Problema de igualdad: Dadas dos gramáticas de libre contexto G1 y G2, ¿L(G1)=L(G2)? Respuesta: NO EXISTE algoritmo general que responda esa pregunta. Por lo tanto, es un problema de decisión indecidible. Ese tema ya viene.

159 Un poco más sobre parseo
Los LLC nos interesan principalmente porque se usan. Y para usarlos, hay que parsearlos. ¿Cuando se parsean LLC? Respuesta: al procesar... Lenguajes de programación. Lenguajes de marcas ("ML") Lenguajes humanos (casi, casi) Veamos el caso de los "ML"

160 HTML como GLC El lenguaje de marcas (markup language) más conocido: HTML. <html> <body> <h4>Un ejemplo de listas anidadas:</h4> <ul> <li>Cafe</li> <li>Te <li>Te negro</li> <li>Te verde</li> </ul> </li> <li>Leche</li> Ese fue un <br> ejemplo. </body> </html> Un ejemplo de listas anidadas: Cafe Te Te negro Te verde Leche Ese fue un ejemplo.

161 HTML como GLC <ul> <li>Cafe</li> <li>Te <li>Te negro</li> <li>Te verde</li> </ul> </li> <li>Leche</li> Ese fue un <br> ejemplo. Hay marcas que comienzan y cierran entornos: <tag> ... </tag> Algunos tags van solos, como el <br>. Claramente hay una estructura gramatical.

162 HTML como GLC Doc   | Elemento Doc
<ul> <li>Cafe</li> <li>Te <li>Te negro</li> <li>Te verde</li> </ul> </li> <li>Leche</li> Ese fue un <br> ejemplo. Doc   | Elemento Doc Elemento  Texto | <ul>Lista</ul> Texto   | Caracter Texto Caracter  <br> | a | b | ... | z | A | B... Lista   | ElementoLista Lista ElementoLista  <li>Doc</li> | <li>Doc Pues HTML no exige el </li> de cierre. Pero lo recomienda, para simplificar el parseo.

163 Marcado semántico Solían usarse los tags del html para darle formato al texto: <i>...</i> ponía texto en itálicas, <b>...</b> lo ponía en negritas, etc. En HTML más reciente, se prefiere una marcación semántica. Por ejemplo <em>...</em> para una frase con énfasis. Luego al desplegar el documento se puede usar itálicas (por ejemplo) para indicar ese énfasis. Ventajas: La forma (CSS) y el contenido (HTML) se especifican por separado. La marcación semántica permite lectura vía software.

164 Marcado semántico <H1>Hot Cop</H1> <i> by Jacques Morali, Henri Belolo</i> <ul> <li>Producer: Jacques Morali <li>Publisher: PolyGram Records <li>Length: 6:20 <li>Written: 1978 <li>Artist: Village People </ul> En el XML se generaliza la idea, para todo tipo de información. <SONG> <TITLE>Hot Cop</TITLE> <COMPOSER>Jacques Morali</COMPOSER> <COMPOSER>Henri Belolo</COMPOSER> <COMPOSER>Victor Willis</COMPOSER> <PRODUCER>Jacques Morali</PRODUCER> <PUBLISHER>PolyGram Records</PUBLISHER> <LENGTH>6:20</LENGTH> <YEAR>1978</YEAR> <ARTIST>Village People</ARTIST> </SONG> <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl=" <xsl:template match="/"> <html> <head><title>Song</title></head> <body><xsl:value-of select="."/></body> </html> </xsl:template> <xsl:template match="TITLE"> <h1><xsl:value-of select="."/></h1> ...

165 XML El XML (eXtensible Markup Language) se usa mucho para comunicar y almacenar información en la web. XHTML (HTML post XML) Feeds RSS Documentos (p.ej., docx o xmlx de Microsoft) Archivos gráficos (SVG) Música; matemáticas; wikipedia; transacciones financieras; servicios web; etc etc <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" " <svg width="100%" height="100%" version="1.1" xmlns=" <rect x="20" y="20" rx="20" ry="20" width="250" height="100" style="fill:red;stroke:black;stroke-width:5;opacity:0.5"/> </svg>

166 XML y DTD De esa misma flexibilidad se deduce que no es un lenguaje; es más bien una forma de definir un lenguaje. El lenguaje mismo se define en el DTD (Document Type Definition). Cualquiera puede inventar su XML, y publicar el DTD. Por eso es buena costumbre incluir una referencia al DTD en el XML. <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" " <svg width="100%" height="100%" version="1.1" xmlns=" <rect x="20" y="20" rx="20" ry="20" width="250" height="100" style="fill:red;stroke:black;stroke-width:5;opacity:0.5"/> </svg>

167 DTD Y el DTD es... una gramática de libre contexto.
Para ser más precisos: es una GLC extendida : se permite que al lado derecho de las producciones aparezcan expresiones regulares. S  (a+c)* (X+Y) X  a* Y  b* No es difícil demostrar que para una GLC extendida siempre existe una GLC "normal" equivalente. Nos deshacemos de las ER agregando variables y producciones.

168 DTD El DTD es una gramática de libre contexto. Forma:
<!DOCTYPE nombre-del-DTD [ lista de definiciones de elementos ]> Definición de elemento: <!ELEMENT nombre-del-elemento (descripción)>

169 DTD La descripción es una expresión regular: Base:
Los nombres de otros elementos #PCDATA, es cualquier texto Operadores: | unión , concatenación * clausura de Kleene: 0 o más ocurrencias + clausura positiva: 1 o más ocurrencias ? opcional: 0 o 1 ocurrencias

170 DTD Ejemplo DTD: <!DOCTYPE PcSpecs [ <!ELEMENT PCS (PC*)>
<!ELEMENT PC (MODEL, PRICE, PROC, RAM, DISK+)> <!ELEMENT MODEL (#PCDATA)> <!ELEMENT PRICE (#PCDATA)> <!ELEMENT PROC (MANF, MODEL, SPEED)> <!ELEMENT MANF (#PCDATA)> <!ELEMENT SPEED (#PCDATA)> <!ELEMENT RAM (#PCDATA)> <!ELEMENT DISK (HARDDISK | CD | DVD )> <!ELEMENT HARDDISK (MANF, MODEL, SIZE)> <!ELEMENT SIZE (#PCDATA)> <!ELEMENT CD (SPEED)> <!ELEMENT DVD (SPEED)> ]>

171 DTD Fragmento de un XML según ese DTD <PCS> <PC>
<MODEL>4560</MODEL> <PRICE>$2295</PRICE> <PROCESSOR> <MANF>Intel</MANF> <MODEL>Pentium</MODEL> <SPEED>4Ghz</SPEED> </PROCESSOR> <RAM>8192</RAM> <DISK> <HARDDISK> <MANF>Maxtor</MANF> <MODEL>Diamond</MODEL> <SIZE>2000Gb</SIZE> </HARDDISK> </DISK> <DISK><CD><SPEED>32x</SPEED></CD></DISK> </PC> <PC> ….. </PC> </PCS> Fragmento de un XML según ese DTD

172 XML Moralejas: Las gramáticas sirven para hacer legibles (humanamente, o automáticamente) conjuntos de datos de casi cualquier tipo. Por facilidad de parseo, lo que se usa son GLC (y rara vez hace falta más). Todo lenguaje decente hoy por hoy tiene una (o más) buena librería para parsear xml. El XML es nuestro amigo.

173 Parseo y compiladores El otro uso (más clásico, pero 100% vigente) de las GLC: lenguajes de programación. Análisis léxico ("lexer") T o t a l = p r e c i + v ; Total = precio + iva ; asignación Análisis sintáctico ("parser") Expresión Total := id id Análisis semántico (chequeo de tipos, etc) precio iva Generación de código

174 Parseo y compiladores Parsear un string: determinar acaso pertenece al lenguaje de la gramática, y si es así, determinar un árbol de derivación. El algoritmo CYK es demasiado lento para parsear código, y no hay algoritmos más rápidos para GLC arbitrarias.  Lo que se hace es usar GLC que se puedan parsear con algoritmos más rápidos. Para efectos prácticos, eso se traduce en LLC deterministas.

175 Parseo y compiladores Dos aproximaciones: Top down:
Partimos de la raíz del árbol de derivación (S) Tomamos una regla de producción, e intentamos calzarla con el input. Si en algún momento no podemos avanzar, nos devolvemos por la rama y probamos otra cosa (backtracking). Bottom up: Vamos procesando las hojas, creciendo hacia la raíz. A medida que leemos el input, los posibles árboles se codifican en un estado interno.

176 Parseo y compiladores Dentro de bottom up, parseadores LR(1):
L: lee el input de izquierda (L) a derecha. R: reproduce una derivación derecha (R). 1: puede "espiar" 1 símbolo hacia adelante. LR(k): idem, pero espiando k símbolos. ¿Qué significa eso?

177 Parseo y compiladores S  Tc T  TA | A A  aTb | ab
Input Acción a ab A T Ta Taa Taab TaA TaT TaTb TA Tc S abaabbc baabbc aabbc abbc bbc bc c shift reduce Leemos el input de izquierda a derecha. Vamos guardándolo en una pila (pasos "shift"). Si se detecta en la pila algo que corresponde al lado derecho de una producción, se reemplaza (pasos "reduce").

178 Parseo y compiladores S  Tc T  TA | A A  aTb | ab
Input Acción a ab A T Ta Taa Taab TaA TaT TaTb TA Tc S abaabbc baabbc aabbc abbc bbc bc c shift reduce Si desandamos lo andado, lo que tenemos es una derivación derecha. S T A T T A A a b a a b b c

179 Parseo y compiladores ¿Qué acción realizar en cada paso?
Decimos que una palabra w es un "item válido completo" si es el lado derecho de alguna producción de la gramática ("aTb"). Decimos que es un "item válido incompleto" si es un prefijo de un ítem válido ("aT"). Entonces, hay dos casos en que la acción está clara.

180 Parseo y compiladores  Si hay un único item válido, y está completo, reemplazarlo (REDUCE).  Si no hay ningún item válido completo, seguir leyendo (SHIFT). Casos problemáticos: Más de un item válido completo (conflicto R/R, dos opciones de reduce). Algunos items válidos completos, otros incompletos (conflicto S/R: ¿shift o reduce?).

181 Parseo y compiladores Aquí entra la cantidad de símbolos:
Un parseador LR(k) "espía" los primeros k símbolos del input restante, y usa eso para resolver el conflicto. Una gramática LR(k) es una que admite un parseador de ese tipo: con k símbolos todo conflicto se resuelve. En particular, una gramática LR(0) es una en que el conflicto jamás se produce.

182 Parseo y compiladores Entonces, parser LR(k):
L: lee el input de izquierda (L) a derecha. R: reproduce una derivación derecha (R). k: puede "espiar" k símbolo hacia adelante. Se puede demostrar que: Toda gramática LR(k), k>1, se puede convertir en una gramática LR(1) equivalente. L es un LLC determinista  L admite una gramática LR(k), para algún k.

183 Lenguajes de libre contexto (LLC)
Parseo y compiladores Lenguajes Lenguajes de libre contexto (LLC) Java, Perl, Python, etc... LLC deterministas LR(0) LR(1) Lenguajes regulares

184 YACC Tiempo atrás mencionamos LEX, que a partir de una lista de expresiones regulares y código asociado, genera un analizador léxico. YACC es el hermano de LEX: a partir de una gramática y código asociado, genera un analizador sintáctico (un parseador). YACC: "yet another compiler compiler".

185 YACC El programa generado por YACC recibirá como input los tokens producidos por el programa generado por LEX. Compilador (o lo que queramos hacer) ER + código GLC + código La parte que uno tiene que hacer LEX YACC ER + código GLC + código "lexer" "parser" input tokens árbol Compilador (o lo que queramos hacer)

186 YACC En algunos casos el código que asociamos al parsear puede ser suficiente para nuestros fines; en esos casos no necesitamos hacer nada más con el output del parser. YACC genera un parser LALR(1): es LR(1), optimizado. Por lo tanto, nuestra gramática debiera ser LR(1). Si no, habrá conflictos sin resolver. Se pueden especificar precedencias para resolver conflictos (la alternativa es cambiar la gramática).

187 YACC YACC informa sobre los conflictos (R/R, S/R); eso ayuda a evitarlos! En ayudantía verán más sobre LEX y YACC; la tarea 2 consiste en usarlos.


Descargar ppt "Lenguajes de libre contexto"

Presentaciones similares


Anuncios Google