Nancy Vazquez Rivera Yesenia Perez Reyes TEORIA DE LA COMPUTACIÓN UNIDAD 3 LENGUAJES LIBRES DE CONTEXTO INTRODUCCIÓN El concepto de Gramática Formal adquirió gran importancia para el desarrollo de lenguajes de programación, consiguientemente el desarrollo de autómatas y máquinas de Túring, creando máquinas cada vez más sofisticadas. La flexibilidad proporcionada por las gramáticas de contexto libre es tal que es la más usada para definir la sintaxis de los lenguajes de programación. Una definición formal de una gramática de contexto sensitivo es la siguiente: Es un cuádruplo G= (V, S , P, S) donde V es un conjunto finito de variables, S es un conjunto finito de símbolos terminales, P es un conjunto finito de reglas y S es el símbolo inicial. 3.1.- GRAMÁTICA LIBRES DE CONTEXTO. Dramática regulares. Las expresiones regulares y los autómatas finitos nos proporcionan dos medios para especificar o definir lenguajes. Las expresiones regulares nos proporcionan una plantilla o patrón para las cadenas de lenguaje. Todas las cadenas se corresponden con un patrón en particular y dichas cadenas serán las únicas que conformaran dicho lenguaje. Un autómata finito especifica un lenguaje como el conjunto de todas las cadenas que lo hacen pasar el estado inicial a uno de sus estados de aceptación. También se podrá interpretar generador de lenguaje. 1. Reconocer números enteros con signo negativo o sin signo ese mismo autómata les va reconocer el operador más o incremento. Frase.- Sujeto Predicado Sujeto.- Articulo Nombre Predicado.- Verbo Adverbio Articulo.- El/ la. Verbo está Adverbio cerca Nombre perro El perro está cerca. Frase Frase Frase Frase Frase Frase Frase Sujeto Predicado. Artículo Nombre Predicado El nombre predicado. El perro predicado El perro verbo Adverbio El perro está adverbio El perro está cerca GRAMATICA GLC Sea G= (N, T, S, P,) una 4-tupla donde: N= Conjunto de No terminales. T= Conjunto de Terminales. P= Conjunto de producciones. S= Conjunto Inicial. CONVENIOS PARA GLC Son terminales: Primeras minúsculas del abecedario. No terminales: Primeras mayúsculas del abecedario. Derivaciones La idea es considerar una producción como una regla de escritura, donde el no terminal de la izquierda se sustituye por la cadena del lado derecho de la producción. 3.2.- ARBOLES DE DERIVACIÓN. Existen básicamente dos formas de describir cómo en una cierta gramática una cadena puede ser derivada desde el símbolo inicial. La forma más simple es listar las cadenas de símbolos consecutivas, comenzando por el símbolo inicial y finalizando con la cadena y las reglas que han sido aplicadas. Si introducimos estrategias como reemplazar siempre el no terminal de más a la izquierda primero, entonces la lista de reglas aplicadas es suficiente. A esto se le llama derivación por la izquierda. Por ejemplo, si tomamos la siguiente gramática: (1) S → S + S (2) S → 1 y la cadena “1 + 1 + 1″, su derivación a la izquierda está en la lista [ (1), (1), (2), (2), (2) ]. Análogamente, la derivación por la derecha se define como la lista que obtenemos si siempre reemplazamos primero el no terminal de más a la derecha. En ese caso, la lista de reglas aplicadas para la derivación de la cadena con la gramática anterior sería la [ (1), (2), (1), (2), (2)]. La distinción entre derivación por la izquierda y por la derecha es importante porque en la mayoría de analizadores la transformación de la entrada es definida dando una parte de código para cada producción que es ejecutada cuando la regla es aplicada. De modo que es importante saber qué derivación aplica el analizador, por que determina el orden en el que el código será ejecutado. Una derivación también puede ser expresada mediante una estructura jerárquica sobre la cadena que está siendo derivada. Por ejemplo, la estructura de la derivación a la izquierda de la cadena “1 + 1 + 1″ con la gramática anterior sería: S→S+S (1) S→S+S+S (1) S→1+S+S (2) S→1+1+S (2) S→1+1+1 (2) { { { 1 }<sub>S</sub> + { 1 }<sub>S</sub> }<sub>S</sub> + { 1 }<sub>S</sub> }<sub>S</sub> donde { … }<sub>S</sub> indica la subcadena reconocida como perteneciente a S. Esta jerarquía también se puede representar mediante un árbol sintáctico: S /| /| / | S ‘+’ S /|\ | /|\ | S ‘+’ S ‘1′ | | ‘1′ ‘1′ La derivación por la derecha: :S→ S + S (1) :S→ 1 + S (2) :S→ 1 + S + S (1) :S→ 1 + 1 + S (2) :S→ 1 + 1 + 1 (2) define el siguiente árbol sintáctico: S /| /| / | S ‘+’ S | /| | /| ‘1′ S ‘+’ S | | ‘1′ ‘1′ Toda derivación de las gramáticas de tipo 1, 2 ó 3 se puede representar mediante un árbol. Una gramática es ambigua si el lenguaje que define contiene alguna cadena que tenga más de un árbol de análisis sintáctico. (No ambigua implica un único árbol) No es posible construir analizadores eficientes para gramáticas ambiguas. Con ciertas restricciones a la gramática se podrá afirmar que no es ambigua. 3.3.- FORMA NORMAL DE CHOMSKY. El hecho de no restringir la forma de las reglas de tipo 2 tiene interés en los casos en que se desea diseñar una gramática para un lenguaje dado. Sin embargo, cuando se desea desarrollar demostraciones de ciertas propiedades de los lenguajes incontextuales o se desea desarrollar algoritmos eficientes que operen sobre gramáticas incontextuales, interesa imponer ciertas restricciones en las formas de las reglas de la gramática. Para ello se introducen las definiciones y los algoritmos de obtención de las formas normales para las gramáticas incontextuales. En concreto, vamos a estudiar la conocida como Forma Normal de Chomsky (FNC). El objetivo principal de esta práctica es el estudio e implementación de los algoritmos que permiten obtener una gramática incontextual en FNC a partir de una gramática incontextual sin λ-reglas ni reglas simples. Diremos que una gramática incontextual G=(N,T,P,S) que no genera la cadena vacía, está en FNC cuando todas sus reglas son de la forma: A → BC con A,B,C ∈ N A → a, con A,B ∈ N y a ∈T Teorema. Todo lenguaje incontextual L que no incluye la cadena vacía, es generado por una gramática en FNC. Ejemplo: Sea la gramática incontextual G definida por las siguientes reglas: S → bA | aB A → bAA | aS | a B → aBB | bS | b Tras la aplicación del PASO 1 se obtiene la gramática en FNC intermedia G': S → CbA | CaB A → CbAA | CaS | a B → CaBB | CbS | b Ca → a Cb → b A partir de G', tras el PASO 2 se obtiene la gramática en FNC G'': S → CbA | CaB A → CbD1 | CaS | a D1 → AA B → CaD2 | CbS | b D2 → BB Ca → a Cb → b 3.4.-FORMA NORMAL DE GREIBACH. Veremos otra posible normalización de gramáticas que nos sirve más adelante para construir cierto tipo de autómatas. Una gramática es en forma normal de Greibach (FNG) si (es decir, su ) solamente contiene variables útiles todas las producciones de (es decir, en su ) son de la forma donde y , es decir, todas las reglas tienen como primer símbolo en sus partes derechas un símbolo terminal que es seguido por una palabra de variables. (porque así no se podría derivar ) si (es decir, el símbolo inicial de ) no aparece al lado derecho de una producción, también está permitido que Obviamente cualquier gramática en forma normal de Greibach es una gramática libre de contexto que se verifica directamente analizando la forma de producciones permitidas. Una interesante propiedad es: para cualquier lenguaje libre de contexto existe una gramática en forma normal de Greibach, que genera el lenguaje. La comprobación de este hecho detallamos con la siguiente construcción, donde a partir de una gramática libre de contexto dada elaboramos una nueva gramática en forma normal de Greibach. Sea un lenguaje libre de contexto y decir una gramática que genere (es ). La construcción sigue 4 pasos (asumimos que , eso remediamos al final): 1. construimos una gramática equivalente en forma normal de Chomsky 2. sustituimos las reglas recursivas a la izquierda, es decir, reglas de tipo 3. establecemos un orden en las variables, es decir de tal manera que todas las reglas serán de tipo con , 4. sustituimos las reglas que no tengan un símbolo terminal como primer símbolo en su parte derecha. Las gramáticas después de cada paso llamamos respectivamente. Usamos la misma gramática inicial como en el apartado anterior donde contenga las siguientes producciones: 3.5.- ELIMINACION DE FACTORES COMUNES IZQUIERDOS Un autómata finito o máquina de estado finito es un modelo matemático de un sistema que recibe una cadena constituida por símbolos de un alfabeto y determina si esa cadena pertenece al lenguaje que el autómata reconoce. Definición formal formalmente, un autómata finito (af) puede ser descrito como una 5-tupla (s,w,t,s,a) donde: w es un alfabeto; s un conjunto de estados; t es la función de transición: ; es el estado inicial; es un conjunto de estados de aceptación o finales. ejemplo 1 w= {0,1}, s = {s1, s2}, t = {(s1,0,{s2});(s1,1,{s1});(s2,0,{s1});(s2,1,{s2})} s = s1 a = {s1}. } Formas de representar un autómata finito además de notar un af a través de su definición formal es posible representarlo a través de otras notaciones que resultan más cómodas. Factorización de términos comunes izquierdos inmediatos. existen gramáticas que tiene producciones de la forma a ¡ å ß1 å ß2 como por ejemplo: s ¡ i e t s e s i e t s donde å es el término común en las producciones de a. sin embargo para poder llevar a cabo el análisis sintáctico de las mismas mediante algunas técnicas se debe eliminar los términos comunes izquierdos llevando a cabo el proceso de factorización siguiente: las producciones a ¡ å ß1 å ß2 se transforman en las siguientes a ¡ å a´ a´¡ ß ß2 las cuales nos generan el mismo lenguaje. Existe un nuevo símbolo no terminal a´ en la gramática, el cual no altera la gramática del lenguaje. 3.6.-Eliminación de recursividad por la izquierda Es posible modificar la gramática para eliminar la recursión por la izquierda. La generalización al caso de recursión por la izquierda no-directa se reduce a la iteración de la solución propuesta para el caso directo. Consideremos una variable con dos producciones: Estas dos producciones pueden ser sustituidas por: eliminando así la recursión por la izquierda. En este caso el procedimiento de eliminación de la recursión por la izquierda lo conduce a: Valor --> E= E --> T E' E' --> +TE' E' --> -TE' E' --> /* vacío, es decir puede ser reemplazado por un espacio o saltado sin problema.*/ T --> FT' T' --> *FT' T' --> F --> (E) F --> número 3.7.- ELIMINACIÓN DE AMBIGÜEDAD Una gramática es ambigua si permite construir dos o más árboles de derivación distintos para la misma cadena. Por lo tanto, para demostrar que una gramática es ambigua lo único que se necesita es encontrar una cadena que tenga más de un árbol de derivación. Una gramática en la cual, para toda cadena generada w, todas las derivaciones de w tienen el mismo árbol de derivación es no ambigua. En algunos casos, dada una gramática ambigua, se puede encontrar otra gramática que produzca el mismo lenguaje pero que no sea ambigua. Si todas las gramáticas independientes del contexto para un lenguaje son ambiguas, se dice que el lenguaje es un lenguaje independiente del contexto inherentemente ambiguo. AMBIGÜEDAD: Sea G = { N , T , P , S } una gramática libre de contexto y sea L(G) el lenguaje generado por esa gramática. Si existe un string w (donde w oe L(G) ) para el cual existen dos ó más formas de realizar la derivación de más a la izquierda (S ˘ w) ó existen dos ó más formas de realizar la derivación de más a la derecha (S ¿ w), entonces se dice que: G es una gramática ambigua. TIPOS DE AMBIGÜEDAD: Dentro del estudio de gramáticas existen dos tipos fundamentales de ambigüedad, los cuales son: Ambigüedad Inherente: Las gramáticas que presentan este tipo de ambigüedad no pueden utilizarse para lenguajes de programación, ya que por más transformaciones que se realicen sobre ellas, NUNCA se podrá eliminar completamente la ambigüedad que presentan. Ambigüedad Transitoria: Este tipo de ambigüedad puede llegar a ser eliminada realizando una serie de transformaciones sobre la gramática original. Una vez que se logra lo anterior, la gramática queda lista para ser reconocida por la mayor parte de los analizadores sintácticos. (Se le considera "ambigüedad" porque existen métodos para realizar análisis sintáctico que no aceptan gramáticas con estas características). 3.8- AUTOMATAS PUSH-DOWN Un autómata de pila o Push-Down es un autómata que cuenta con un mecanismo que permita almacenamiento ilimitado y opera como una pila. El autómata de pila (se abrevia PDA de sus siglas en inglés Push-Down Autómata) tiene una cinta de entrada, un control finito y una pila. La pila es una cadena de símbolos de algún alfabeto. El símbolo que se encuentra más a la izquierda se considera como que está en la “cima”. El dispositivo será no determinístico y tendrá un número finito de alternativas de movimiento en cada situación. 3.9.- Lenguajes No Regulares •Existen lenguajes que no son regulares y técnicas para demostrarlo: “El lema de bombeo” Ejemplo:L ={ 0n1n: n ≥0} no es regular Idea de la demostración: •Si L es regular, existe M = (Q, Σ, δ, q0, F) un AFDt que lo reconoce. Además, M tiene un nofinito de estados. •Deben existir 0iy 0jcon i ≠j tales que δ*(q0, 0i) = δ*(q0, 0j) •Esto significa que δ*(q0, 0i1i) = δ*(q0, 0j1i), pero por un lado 0i1i ∈L y por otro 0j1i ∉L. Llegamos a un contradicción.