Procesamiento de Lenguajes (PL) Curso 2014/2015 Conceptos básicos sobre gramáticas Gramáticas y lenguajes Gramáticas Dado un alfabeto Σ, un lenguaje es un conjunto (finito o infinito) de cadenas de símbolos pertenecientes al alfabeto. Es posible que la cadena vacía, ɛ, pertenezca a un lenguaje Una gramática es una forma compacta de representar un lenguaje IMPORTANTE: Una gramática genera un único lenguaje, pero un mismo lenguaje puede ser generado por varias gramáticas Una gramática es una cuádrupla G = (V N, V T, S, P ), donde V N es el conjunto de símbolos variables o no terminales, V T es el conjunto de símbolos terminales (todos los terminales deben pertenecer al alfabeto), S es el símbolo inicial de la gramática (S siempre es un ), y P es el conjunto de producciones o reglas de la gramática Producciones y derivaciones Una producción o regla de una gramática tiene una parte izquierda y una parte derecha. Tanto la parte izquierda como la parte derecha son una cadena de símbolos terminales y es Normalmente, solamente se especifica el conjunto de producciones P, y se asume que el símbolo inicial de la gramática es la parte izquierda de la primera producción. Ejemplo: A a B C B b bas B big C boss C ɛ C c G = (V N = A, B, C, V T = a, b, bas, big, boss, c, S = A, P = A...) Una derivación es una secuencia de cadenas de símbolos (llamadas formas sentenciales) en la que cada cadena es resultado de la aplicación de una regla de la gramática a la cadena anterior. Una derivación válida es aquella en la que la primera cadena de la secuencia es el símbolo inicial, y la última es una cadena de terminales. Ejemplos: A a B C a B c a big C boss c a big boss c A a B C a b bas C a b bas En los ejemplos, la primera derivación se dice que es una derivación por la derecha, porque siempre se deriva el situado más a la derecha, y la segunda es una derivación por la izquierda IMPORTANTE: El lenguaje generado por una gramática es el conjunto de cadenas de terminales obtenidas a partir de derivaciones válidas usando las reglas de la gramática Un árbol de derivación es un árbol en el que se representa una derivación válida de una cadena (pero no se especifica el orden en que se han aplicado las reglas)
PL, 2013/2014 2 A a B C b bas ε Características especiales Se dice que una gramática es ambigua cuando, para una cadena determinada, existe más de un árbol de derivación IMPORTANTE: la única forma de saber si una gramática es ambigua es encontrando una cadena con más de un árbol de derivación, no hay otra forma Ejemplo: E E opsuma E E num La cadena 2+3-4 tiene dos árboles de derivación, y la cadena 2+3-4+5 tiene más de dos árboles ( cuántos?) Una gramática se dice que es recursiva por la izquierda si tiene al menos una regla de esta forma: E E opsuma T De forma similar, una gramática puede presentar recursividad por la derecha: E T opsuma E Una gramática se dice que tiene factores comunes por la izquierda si tiene en al menos dos reglas (con la misma parte izquierda) símbolos comunes al principio de la parte derecha de la regla: Jerarquía de gramáticas A B a C A B a d... Según la forma de las producciones, las gramáticas se clasifican en: Regulares: en la parte izquierda sólo hay un, y la parte derecha puede haber: terminal terminal ɛ Independientes del contexto (context-free): en la parte izquierda sólo hay un, en la parte derecha no hay restricciones Dependientes del contexto: en la parte izquierda puede haber terminales y es, pero al menos debe haber un, y la longitud de la parte derecha debe ser mayor o igual que la de la parte izquierda No restringidas
PL, 2013/2014 3 Qué tipos de gramáticas se usan en los compiladores? En los compiladores se utilizan solamente gramáticas regulares y gramáticas independientes del contexto (GIC) Las gramáticas regulares se utilizan para especificar los tokens (en realidad, se utilizan expresiones regulares, pero son equivalentes) Las GIC se utilizan para especificar la sintaxis de las construcciones del lenguaje fuente En los lenguajes de programación hay restricciones semánticas (p.ej. es necesario haber declarado una variable antes de utilizarla), que hacen que en realidad los lenguajes de programación sean lenguajes sensibles al contexto, pero no se utilizan gramáticas sensibles al contexto, se utilizan GIC a las que se añaden acciones para la comprobación de las restricciones semánticas. Diseño de gramáticas para expresiones en lenguajes de programación Un buen diseño de la gramática nos permitirá reflejar de forma natural características semánticas del lenguaje en el árbol de derivación, y esto permitirá que la traducción sea más sencilla Es importante por tanto diseñar una buena gramática, pero luego es posible que se tenga que modificar según el tipo de analizador sintáctico que se desee utilizar Además, es posible que al diseñar el proceso de traducción sea necesario rediseñar la gramática para facilitar el diseño del traductor Diseño de gramáticas para expresiones: asociatividad La asociatividad indica cómo se agrupan los operandos en un operador cuando aparecen más de dos operandos. Por ejemplo, 4-3-2 normalmente vale -1, porque el operador - suele tener asociatividad por la izquierda, y primero se evalua 4-3 y al resultado se le resta 2. Sin embargo, si la asociatividad fuera por la derecha, el resultado sería 3 ( 4-(3-2) ) Los paréntesis permiten alterar la asociatividad por defecto de un operador Cómo se puede reflejar la asociatividad en una gramática? asociatividad izquierda E E opsuma T asociatividad derecha E T opsuma E Pero... no son todos los operadores asociativos por la izquierda? Sí, casi todos, pero no todos: a=b=c=0 Diseño de gramáticas para expresiones: precedencia En la mayoría de los lenguajes de programación, unos operadores se evaluan antes que otros. Por ejemplo, 2+3*4 casi siempre vale 14. Como ocurre con la asociatividad, los paréntesis permiten alterar la precedencia de los operadores: (2+3)*4 Cómo se puede reflejar la precedencia en una gramática? con un diferente para cada nivel de precedencia: Expr Expr or EBool Expr EBool EBool EBool and ExpRel EBool ExpRel ExpRel E oprel E E E opsuma T E T T T opmul F T F F... Algunos operadores no permiten usar más de dos operandos, como por ejemplo: a<b<c a<b && b<c
PL, 2013/2014 4 Tabla de asociatividades y precedencias Es una tabla en la que aparecen los operadores y su asociatividad, ordenados de menor a mayor precedencia: Operador Asociatividad @ izquierda % derecha # izquierda A A @ B A B B C % B B C C C # D C D D... Operadores unarios Los operadores unarios son más difíciles de reflejar en una gramática, requiere un buen conocimiento del lenguaje y de las gramáticas Los ejemplos más conocidos son: el operador de negación,!, que además permite que se repita el operador:!!!true ExpRel! ExpRel el operador de cambio de signo, - o +, que no permite repeticiones (por ejemplo, ----3 no es correcto) E opsuma T El problema del dangling- Con qué if se asocia el? La regla que se usa normalmente es asociar el al if más cercano: Consejo: es mejor usar bloques entre llaves de forma explícita
PL, 2013/2014 5 Fragmento de gramática para la instrucción if : Tiene dos características no deseables: 1. Tiene factores comunes por la izquierda 2. Es ambigua!!!!! Instr id opasig Expr Instr... Instr if ( Expr ) Instr Instr if ( Expr ) Instr Instr... y sin embargo, se utiliza en prácticamente todos los compiladores basados en análisis sintáctico ascendente (los que usan yacc o bison).