Lenguajes de libre contexto Lenguajes de libre contexto
Lenguajes de libre contexto
Lenguajes de libre contexto 1. Gramáticas, GLC 2. Ambigüedad. Simplificación. 3. Formas normales y parseo 4. Autómatas de pila; AP GLC 5. Lema de bombeo para GLC 6. Propiedades de clausura 7. Problemas de decisión 8. Complementos
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
Gramáticas Una derivación de "un niño corre": O S P S A T P V A el A un T niño T perro V corre V camina O S P A T V un T corre un niño corre
Gramáticas O S P 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"}
Gramáticas T niño Variable o "noterminal" Regla de producción Terminal
Gramáticas Otro ejemplo: Lenguaje: Algunas derivaciones: Este formalismo permite describir algunos lenguajes que no son regulares.
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)*.
Derivaciones • T={a, b} • V={S} • S=S • P={S a. Sb, 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.
Derivaciones Definimos * como la cerradura transitiva de . Es decir, u * v ssi u 1, u 2, . . . , uk tales que u u 1 u 2 . . . 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
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 corchetes: S. Casos típicos: cuando es inevitable usar mayúsculas en los terminales, o cuando una variable tiene un nombre natural (ejemplo: predicado ).
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.
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!!
(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 a. T ó 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.
Gramáticas regulares L es un lenguaje regular ssi L=L(G) para una gramática regular G. Ir derivando la palabra corresponde a ir cambiando de estado interno (la variable), y escribiendo.
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 a. T • T Sb • S genera {anbn, n 0}, que es el clásico ejemplo de lenguaje no regular.
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 “|”:
GLC, ejemplos Más ejemplos: donde T={(, )}, V={S}. ¿Lenguaje? S (S) () S (S) (SS) ((S)(S)) (()()) da el lenguaje de los paréntesis bien balanceados.
GLC, ejemplos Más ejemplos: donde T={a, b}, V={S}. ¿Lenguaje? S a. Sa ab. Sba abb. Sbba abbbba es el lenguaje de los palíndromes de largo par: L={ w=u u. R, u {a, b}*} Ejercicio: modificar la gramática para que también genere palíndromes de largo impar (p. ej. , aba).
GLC, ejercicios Ejercicios: • ¿Qué lenguajes describen las siguientes gramáticas? G 1: S Xaa. X X a. X | b. X | G 2: S XY X a. X | b. X | 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}.
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 a. Sb El de los palíndromes es análogo. Idea: hay un "surtidor" al medio que emite letras de manera simétrica.
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=L 1 L 1, con L 1={anbn, n 0}, de modo que X e Y pueden ser el mismo: S XX X a. Xb |
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 a. Sb | X X b. Xa |
GLC, un ejemplo más complejo L={w {0, 1}*: w tiene dos bloques maximales de 0’s del mismo tamaño}. Permitidos: 01011, 001011001, 10010101001 No permitidos: 01001000, 01111 10010011010010110 inicio A parte central B A: , ó termina en 1 C: , ó comienza con 1 final C 00110100 D Cantidad de 0’s: • la misma a cada lado • al menos uno
GLC, un ejemplo más complejo De modo que descomponemos por partes, y luego aplicamos recursividad en B. 10010011010010110 inicio A parte central B 00110100 D Cantidad de 0’s: • la misma a cada lado • al menos uno final C S → ABC A → | U 1 U → 0 U | 1 U | C → | 1 U B → 0 B 0 | 0 D 0 D → 1 U 1 | 1
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 aa. XYb aaaa. Xb aaaab S XY aaaa. Yb aaaab
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 aaaa. Yb 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 aaaab
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 aaaa. Yb aaaab S R aaaab : S XY XYb Xb aaaab
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.
GLC: ambigüedad E E + E | (E) | V V x|y|z E E + E V E + E x V y V z E E+E V+E x+E+E x+V+E x+y+V x+y+z E E + E V V V z x y E E+E+E V+E+E x+V+E x+y+V x+y+z • Aquí hay dos árboles distintos, cada uno con una derivación izquierda distinta.
Lenguajes de libre contexto 1. Gramáticas, GLC 2. Ambigüedad. Simplificación. 3. Formas normales y parseo 4. Autómatas de pila; AP GLC 5. Lema de bombeo para LLC 6. Propiedades de clausura 7. Problemas de decisión 8. Complementos
GLC: ambigüedad T o t a l Total = = p r e c i o + i v a ; precio + iva Analizador léxico ; asignación Parser Total : = Expresión id precio + id iva • Los parseadores construyen árboles de análisis sintáctico. • El árbol indica cómo se entiende el texto.
GLC: ambigüedad 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 E E V E + E x V y V z “Primero y+z, luego x eso” x y+z E E + E E E V V V z x y “Primero x y, luego eso + z”
GLC: ambigüedad Un caso clásico de ambigüedad en lenguajes de programación: dos ifs, un else. S if b then S S else if b then S S if b La mayoría de los lenguajes lo resuelven asignando el else al if más cercano. S then if b then S else S
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.
GLC: ambigüedad • 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 E T|E+T T F|T F F (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.
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”.
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.
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).
Lenguajes de libre contexto 1. Gramáticas, GLC 2. Ambigüedad. Simplificación. 3. Formas normales y parseo 4. Autómatas de pila; AP GLC 5. Lema de bombeo para LLC 6. Propiedades de clausura 7. Problemas de decisión 8. Complementos
GLC: simplificación Dada una gramática G, digamos, S → a. Sb | b. Sa. Sb | 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?
GLC: simplificación S → a. Sb | b. Sa. Sb | T T→S| w=aabbb ¿está? Por fuerza bruta: podemos intentar generar todas las posibles derivaciones, buscando alguna que genere w. b. Sa. Sb S a. Sb T . . . aa. Sbb ab. Sa. Sbb a. Tb ba. Sb S . . . Problema: ¿Cuándo parar?
GLC: simplificación S → a. Sb | b. Sa. Sb | 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 a. Sb ab. Sa. Sbb ab. Sabb ababb Para evitar esos "acortamientos", sería bueno que no hubiera producciones nulas como esa.
GLC: simplificación S → a. Sb | b. Sa. Sb | 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 a. Sb a. Tb a. Sb … Así que sería bueno evitar situaciones de ese tipo también.
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 X 1. . . Xm donde Y no está marcado pero todos los Xi lo están, marcar Y.
Eliminación de producciones nulas Para eliminar las producciones nulas, (1) Determinar todas las variables anulables, X 1, . . . , Xk (2) Para cada producción de la forma Y u. Xiv, 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 u. Xab. Yv, y tanto X como Y son anulables, agregamos • Y uab. Yv • Y u. Xabv • Y uabv
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 .
Eliminación de producciones nulas Ejemplo: S a | Xb | a. Ya 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 a. Ya se agrega S aa, pues Y es anulable, S a | Xb | a. Ya | b | aa X Y Y b|X
Eliminación de producciones nulas Otro ejemplo: Gramática inicial Gramática final Sustituimos Anulables: M
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 a. X | Yb X S Y b. Y | b | X S X - S Y - X S X Y - Y X Y S Y X S
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 s 1, Y s 2, . . . , agregamos X s 1, X s 2, . . . • 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).
Eliminación de producciones unitarias Ejemplo: S a. X | Yb X S Y b. Y | b | X • Como X * S, se agregan X a. X , X Yb • Como Y * X. . . No se agrega nada. • Como Y * S, se agregan Y a. X , Y Yb • Finalmente, eliminamos las producciones unitarias X S y Y X. La nueva gramática quedaría S a. X | Yb X a. X | Yb Y b. Y | b | a. X | Yb
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.
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.
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, S} U={a, b, A, S, B}
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, S} U={a, b, A, S, B}
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.
Eliminación de variables inútiles Simplificación de gramáticas: (1) Eliminar producciones nulas (2) Eliminar producciones unitarias (3) 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).
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.
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.
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:
Lenguajes de libre contexto 1. Gramáticas, GLC 2. Ambigüedad. Simplificación. 3. Formas normales y parseo 4. Autómatas de pila; AP GLC 5. Lema de bombeo para LLC 6. Propiedades de clausura 7. Problemas de decisión 8. Complementos
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
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 quede en FNC. 1. Eliminamos las producciones nulas, unitarias, e inútiles. 2. Eliminamos lados derechos "mixtos":
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.
Forma Normal de Chomsky (FNC) 3. Reemplazamos toda producción de la forma A C 1 C 2. . . Cn por una cadena de producciones A C 1 V 1, V 1 C 2 V 2, . . . , Vn-2 Cn-1 Cn donde los V 1, . . . , Vn-2 son nuevas variables, intermedias.
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|.
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. . .
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=v 1 v 2 se tiene que X * v 1, Y * v 2 (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.
CYK Ejemplo, con gramática: Subpalabras de largo k, partiendo de posición j kj 1 2 3 y palabra 4 5 1 2 3 4 5
CYK Variables que generan las subpalabras de largo 1
CYK Variables que generan las subpalabras de largo 2
CYK Variables que generan las subpalabras de largo 5 (o sea, w)
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|.
CYK Observación: al reconstruir la derivación de la palabra, puede que en algún punto encontremos dos opciones: dos formas distintas (i. e. , con producciones distintas) de generar alguna subpalabra. entonces la gramática es ambigua, y lo que hemos encontrado es un ejemplo de palabra con más de un árbol de derivación.
Lenguajes de libre contexto 1. Gramáticas, GLC 2. Ambigüedad. Simplificación. 3. Formas normales y parseo 4. Autómatas de pila; AP GLC 5. Lema de bombeo para LLC 6. Propiedades de clausura 7. Problemas de decisión 8. Complementos
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.
PDA (Autómatas de pila) Input Estados Pila PDA Al AF le agregamos una pila (stack). Memoria potencialmente infinita, pero de acceso restringido.
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
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+.
PDA (Autómatas de pila) 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 z 0, u otro). Al comienzo es lo único que la pila contiene. En el grafo de transiciones se anota lo que se saca y guarda en la pila: leo del input saco de la pila guardo en la pila Notación alternativa (equivalente)
input pila tope Reemplaza
input pila tope Guarda ("push")
input pila tope Saca ("pop")
input pila tope Sin cambio (con cualquier contenido en la pila)
input pila tope Sin cambio (con el tope de la pila en “b”)
PDA (Autómatas de pila) No determinismo Transición
PDA (Autómatas de pila) Formalmente, un PDA es una tupla M = ( Q, , q 0, F ) • Q es un conjunto finito de estados • es el alfabeto de entrada • es el alfabeto de la pila • q 0 es el estado inicial • F Q son los estados de aceptación • es la función de transición, : Q ( { }) 2 Q ( { })
PDA, ejemplo 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!
Input PDA, ejemplo Pila estado actual
Input PDA, ejemplo Pila
Input PDA, ejemplo Pila
Input PDA, ejemplo Pila
Input PDA, ejemplo Pila
Input PDA, ejemplo Pila
Input PDA, ejemplo Pila
Input PDA, ejemplo Pila
Input PDA, ejemplo Pila acepta
PDA, ejemplo En general, el lenguaje que este PDA acepta es
PDA, otro ejemplo Otro ejemplo, ahora con lenguaje:
Input PDA, otro ejemplo Pila
Input PDA, otro ejemplo Pila
Input PDA, otro ejemplo Pila
Input PDA, otro ejemplo Pila
Input PDA, otro ejemplo Pila
Input PDA, otro ejemplo Pila
Input PDA, otro ejemplo Pila acepta
Un ejemplo más: [transparencia 23] 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, 001011001, 10010101001 Ejemplos de strings fuera de L: 01001000, 01111 S → ABC A → | U 1 U → 0 U | 1 U | C → | 1 U B → 0 B 0 | 0 D 0 D → 1 U 1 | 1 D 10010011010010110 A B C A, parte inicial: , o termina en 1 B, parte central: zona D, rodeada por bloques de 1 o más 0’s. C, parte final: , o comienza con 1 D, centro del centro: parte y termina con 1.
Un ejemplo más: He aquí un PDA que reconoce ese mismo lenguaje: A q 0 , / B 0, / 1, / q 1 0, / 0 1, / 0, / 0 q 2 0, / 1, / q 6 C q 7 q 3 1, / 1, $ / $ q 5 0, 0 / D 1, / q 4 1, / 0, / 1, /
Guardando strings Permitiremos ahora guardar strings en la pila: pila tope Guarda “cda” String guardado string guardado
Guardando strings El poder de cómputo no cambia, pues es equivalente a tener
Y otro ejemplo. . .
Input Pila
Input Pila
Input Pila
Input Pila
Input Pila
Input Pila
Input Pila
Input Pila acepta
• 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.
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 w , lo que falta por leer en el input v , el contenido actual del stack
PDA: descripción instantánea Descripción instantánea Instante 4: Input Pila
PDA: descripción instantánea Descripción instantánea Instante 5: Input Pila
PDA: descripción instantánea Escribimos (q 1, bbb, aaa$) (q 2, bb, aa$) y el cómputo completo será (q 0, aaabbb, $) (q 1, aabbb, a$) (q 1, abbb, aa$) (q 1, bbb, aaa$) (q 2, bb, aa$) (q 2, b, a$) (q 2, , $) (q 3, , $) • Abreviando podemos decir que (q 0, aaabbb, $) (q 3, , $) • A veces se pone * para especificar que se refiere a más de un paso.
Lenguaje por estado de aceptación Podemos entonces definir formalmente el lenguaje aceptado por un PDA M=(Q, , q 0, F) como L(M) = { w *: qf F, v *, (q 0, 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.
Lenguaje por pila vacía Otra forma de asociar un lenguaje a un PDA es definir N(M) = { w *: q Q, (q 0, 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(M 1) para un PDA M 1, existe un PDA M 2 con L=N(M 2), y viceversa:
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 z 0 es un "falso piso", de modo que la pila sólo estará de verdad vacía al llegar a p.
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. , $/Z 0$ , $/
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:
GLC PDA G: S a. Sa | YZ Y b. Y | Z c. Z | Y S a. Sa aa. Saa aa. YZaa aabb. Zaa aabbc. Yaa aabbcbaa Consideremos una derivación izquierda de aabbcbaa con la gramática G. S aa Y aabb Z aabbc Y aabbcb a aa Zaa Zaa aa aa 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!
GLC PDA Sea G=(V, T, S, P) una gramática de libre contexto. Construimos el PDA M=(Q, , q 0, F) con • Q={q 0, q 1, q 2} • F={q 2} • =T • =V T {$} • se construye como sigue: Para cada producción p q en P Para cada a T
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.
GLC PDA S a. Sa a. YZa abc. Ya abcba Ejemplo: derivación izquierda (a la idem), y cómputo en el PDA (a la derecha). Gramática: S a. Sa | YZ Y b. Y | Z c. Z | Y (q 0, abcba, $) (q 1, abcba, S$) (q 1, abcba, a. Sa$) (q 1, bcba, YZa$) (q 1, bcba, b. YZa$) (q 1, cba, c. Za$) (q 1, ba, Ya$) (q 1, ba, b. Ya$) (q 1, a, a$) (q 1, , $) (q 2, , $)
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, , q 0, F) en la forma en que los definimos al comienzo (escribiendo símbolos de a uno en la pila, no strings).
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.
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 [p. Xq]. V = {S} Q ( { }) Q
PDA GLC Las reglas de producción se definen con la intención de que [p. Xq] 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): [p. Xq] * w (p, w, X) * (q, , )
PDA GLC [p. Xq] * w gramática (p, w, X) * (q, , ) pda • Primero se define S [q 0 Z 0 p] para todo p Q. • Ese Z 0 es el "falso piso": al sacarlo, se vacía la pila, y podemos aceptar (si el input se acabó). • Si los [q 0 Z 0 p] cumplen lo de arriba, estamos bien.
PDA GLC A continuación, para cada transición como la de arriba, se agregan las reglas de producción [p. Xr] a [q. Yr] 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 [p. Xr] a [q. Yr].
PDA GLC Para hacer la demostración, se demuestra [p. Xq] * 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.
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 nodeterminismo sí cambia los lenguajes.
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.
PDA y determinismo Un ejemplo de lenguaje que está en LPDA pero no está en LDPDA es {w=u u. R : u {a, b}* } Idea: • Para reconocer 0 n 10 n necesito que el segundo grupo de 0's "mate" al primero en el stack • . . . pero entonces ya no podría reconocer 0 n 10 n, que también está en L.
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
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.
Lema de bombeo para LLC
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| 2 h (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).
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"
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.
Lema de bombeo para LLC, ejemplo Ejemplo: L={anbncn: n 0} no es de libre contexto. • Para que uv 2 xy 2 z 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.
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).
Propiedades de clausura Sean L 1 y L 2 dos lenguajes de libre contexto, descritos por las gramáticas G 1=(V 1, T 1, S 1, P 1) y G 2=(V 2, T 2, S 2, P 2). Entonces: • L 1 L 2 es de libre contexto En efecto, lo genera la gramática G=(V, T, S, P) con • V = V 1 V 2 {S} • T = T 1 T 2 • S una variable nueva • P = P 1 P 2 {S S 1|S 2}
Propiedades de clausura De manera similar, • L 1 L 2 es de libre contexto Tomamos G=(V, T, S, P) con • V = V 1 V 2 {S} • T = T 1 T 2 • S una variable nueva • P = P 1 P 2 {S S 1 S 2}
Propiedades de clausura También se tiene que: • L 1* es de libre contexto Tomamos G=(V, T, S, P) con • V = V 1 {S} • T = T 1 • S una variable nueva • P = P 1 {S S S 1 | }
Propiedades de clausura Por otro lado, • L 1 L 2 no necesariamente es de libre contexto Contraejemplo: L 1={anbncm, n 0, m 0} y L 2={anbmcm, n 0, m 0} son de libre contexto [ejercicio!], pero L 1 L 2 = {anbncn: n 0} no lo es.
Propiedades de clausura También se tiene que • L 1 C 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.
Propiedades de clausura Lo que sí se cumple es que para L 3 regular, L 1 L 3 es de libre contexto Idea de la demostración: • Consideramos M=(Q, , q 0, F) un PDA que reconoce L 1, y M'=(Q', ', q'0, F') un AFD que reconoce L 3. • 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.
Propiedades de clausura Lo que sí se cumple es que para L 3 regular, L 1 L 3 es de libre contexto Aplicaciones de eso: 1) Probemos que L={anbn: n 100} es de libre contexto. Notamos que L = L 1 L 3, donde • L 1 = {anbn} • L 3 = {a, b}* {a 100 b 100} Sabemos que L 1 es un LLC; por otro lado, L 3 es regular. L es regular.
Propiedades de clausura Lo que sí se cumple es que para L 3 regular, L 1 L 3 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.
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).
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.
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.
Problemas de decisión asociados a LLC Problema de finitud:
Problemas de decisión asociados a LLC Problema de igualdad: Dadas dos gramáticas de libre contexto G 1 y G 2, ¿L(G 1)=L(G 2)? Respuesta: • NO EXISTE algoritmo general que responda esa pregunta. • Por lo tanto, es un problema de decisión indecidible. • Ese tema ya viene.
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"
HTML como GLC El lenguaje de marcas (markup language) más conocido: HTML. <html> <body> <h 4>Un ejemplo de listas anidadas: </h 4> <ul> <li>Cafe</li> <li>Te <ul> <li>Te negro</li> <li>Te verde</li> </ul> </li> <li>Leche</li> </ul> Ese fue un ejemplo. </body> </html> Un ejemplo de listas anidadas: • Cafe • Te negro • Te verde • Leche Ese fue un ejemplo.
HTML como GLC Hay marcas que comienzan y cierran entornos: <tag>. . . </tag> Algunos tags van solos, como el . Claramente hay una estructura gramatical. <ul> <li>Cafe</li> <li>Te <ul> <li>Te negro</li> <li>Te verde</li> </ul> </li> <li>Leche</li> </ul> Ese fue un ejemplo.
HTML como GLC Doc | Elemento Doc Elemento Texto | <ul>Lista</ul> Texto | Caracter Texto <ul> <li>Cafe</li> <li>Te <ul> <li>Te negro</li> <li>Te verde</li> </ul> </li> <li>Leche</li> </ul> Ese fue un ejemplo. Caracter | a | b |. . . | z | A | B. . . Lista | Elemento. Lista <li>Doc</li> | <li>Doc Pues HTML no exige el </li> de cierre. Pero lo recomienda, para simplificar el parseo.
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.
Marcado semántico En el XML se generaliza la idea, para todo tipo de información. <H 1>Hot Cop</H 1> <i> by Jacques Morali, Henri Belolo</i> <ul> <li>Producer: Jacques Morali <li>Publisher: Poly. Gram Records <li>Length: 6: 20 <li>Written: 1978 <li>Artist: Village People </ul> <SONG> <TITLE>Hot Cop</TITLE> <COMPOSER>Jacques Morali</COMPOSER> <COMPOSER>Henri Belolo</COMPOSER> <COMPOSER>Victor Willis</COMPOSER> <PRODUCER>Jacques Morali</PRODUCER> <PUBLISHER>Poly. Gram Records</PUBLISHER> <LENGTH>6: 20</LENGTH> <YEAR>1978</YEAR> <ARTIST>Village People</ARTIST> </SONG> <? xml version="1. 0"? > <xsl: stylesheet xmlns: xsl="http: //www. w xsl"> <xsl: template match="/"> <html> <head><title>Song</title></h <body><xsl: value-of select=". "/>< </html> </xsl: template> <xsl: template match="TITLE"> <h 1><xsl: value-of select=". "/></h 1> </xsl: template>. . .
XML El XML (e. Xtensible 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 <? xml version="1. 0" standalone="no"? > <!DOCTYPE svg PUBLIC "-//W 3 C//DTD SVG 1. 1//EN" "http: //www. w 3. org/Graphics/SVG/1. 1/DTD/svg 11. dtd"> <svg width="100%" height="100%" version="1. 1" xmlns="http: //www. w 3. org/2000/svg"> <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>
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 "-//W 3 C//DTD SVG 1. 1//EN" "http: //www. w 3. org/Graphics/SVG/1. 1/DTD/svg 11. dtd"> <svg width="100%" height="100%" version="1. 1" xmlns="http: //www. w 3. org/2000/svg"> <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>
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.
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)>
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
DTD Ejemplo DTD: <!DOCTYPE Pc. Specs [ <!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)> ]>
DTD <PCS> <PC> Fragmento de un XML según ese DTD <MODEL>4560</MODEL> <PRICE>$2295</PRICE> <PROCESSOR> <MANF>Intel</MANF> <MODEL>Pentium</MODEL> <SPEED>4 Ghz</SPEED> </PROCESSOR> <RAM>8192</RAM> <DISK> <HARDDISK> <MANF>Maxtor</MANF> <MODEL>Diamond</MODEL> <SIZE>2000 Gb</SIZE> </HARDDISK> </DISK> <DISK><CD><SPEED>32 x</SPEED></CD></DISK> </PC> <PC> …. . </PC> </PCS>
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 parsear xml. • El XML es nuestro amigo.
Parseo y compiladores El otro uso (más clásico, pero 100% vigente) de las GLC: lenguajes de programación. T o t a l Total = = p r e c i o + i v a ; precio + iva ; asignación Total Análisis semántico (chequeo de tipos, etc) Generación de código Expresión : = id precio Análisis léxico ("lexer") + id iva Análisis sintáctico ("parser")
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.
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.
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?
Parseo y compiladores S Tc T TA | A A a. Tb | ab • 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"). Pila Input Acción a ab A T Ta Taab Ta. A Ta. Tb TA T Tc S abaabbc aabbc bbc bc c c c shift reduce shift reduce
Parseo y compiladores S Tc T TA | A A a. Tb | ab • Si desandamos lo andado, lo que tenemos es una derivación derecha. S T A T A a b a a b b c Pila Input Acción a ab A T Ta Taab Ta. A Ta. Tb TA T Tc S abaabbc aabbc bbc bc c c c shift reduce shift reduce
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 ("a. Tb"). • Decimos que es un "item válido incompleto" si es un prefijo de un ítem válido ("a. T"). Entonces, hay dos casos en que la acción está clara.
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? ).
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.
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.
Parseo y compiladores Lenguajes de libre contexto (LLC) LLC deterministas LR(1) LR(0) Lenguajes regulares Java, Perl, Python, etc. . .
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".
YACC El programa generado por YACC recibirá como input los tokens producidos por el programa generado por LEX. input ER + código GLC + código LEX YACC "lexer" tokens "parser" árbol La parte que uno tiene que hacer Compilador (o lo queramos hacer)
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).
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. • http: //www. giaa. inf. uc 3 m. es/docencia/II/PL 2/herra mientas/YACC. pdf • http: //epaperpress. com/lexandyacc/ • http: //dinosaur. compilertools. net/
- Slides: 194