Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Procesadores de Lenguajes I

528 views

Published on

Material Completo de la asignatura Procesadores de Lenguajes I correspondiente al Grado en Ingeniería Informática impartido por la UNED

Published in: Education
  • Be the first to comment

  • Be the first to like this

Procesadores de Lenguajes I

  1. 1. Procesadores de Lenguajes Ingeniería Técnica superior de Ingeniería Informá8ca Departamento de Lenguajes y Sistemas informá6cos Javier Vélez Reyes jvelez@lsi.uned.es Departamento de Lenguajes Y Sistemas Informá6cos UNED Universidad Nacional de Educación a Distancia Grado en Ingeniería Informática
  2. 2. Procesadores de Lenguajes Ingeniería Técnica superior de Ingeniería Informá8ca Departamento de Lenguajes y Sistemas informá6cos Javier Vélez Reyes jvelez@lsi.uned.es Departamento de Lenguajes Y Sistemas Informá?cos UNED Parte III Etapa de síntesis
  3. 3. Procesadores de Lenguajes Ingeniería Técnica superior de Ingeniería Informá8ca Departamento de Lenguajes y Sistemas informá6cos Javier Vélez Reyes jvelez@lsi.uned.es Departamento de Lenguajes Y Sistemas InformáAcos UNED 6 Análisis semán*co I Traducción dirigida por la sintaxis
  4. 4. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 2 Obje6vos Generales Obje8vos Generales ›  Aprender qué es la semántica de un lenguaje de programación ›  Entender cómo funciona la semántica operacional y distinguirla de otros tipos de semántica ›  Aprender cómo se inyecta información semántica en una gramática ›  Aprender el principio de traducción dirigido por la sintaxis ›  Conocer las gramáticas con atributos y las reglas y acciones semánticas ›  Aprender a distinguir los diferentes tipos de atributos que existen ›  Aprender a construir y analizar grafos de dependencia ›  Aprender a identificar compiladores de simple y doble pasada ›  Conocer los distintos tipos de formalismos de traducción que existen ›  Aprender a especificar definiciones dirigidas por la sintaxis ›  Aprender a especificar esquemas de traducción dirigidos por la sintaxis
  5. 5. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 3 Índice Índice ›  Introducción ›  Formalismos para la especificación de traducciones ›  Definiciones dirigidas por la sintaxis ›  Reglas semánticas ›  Atributos ›  Grafos de dependencia ›  Diseño de definiciones dirigidas por la sintaxis ›  Gramáticas con atributos por la izquierda ›  Esquemas de traducción dirigidas por la sintaxis ›  Acciones semánticas ›  Atributos ›  Diseño de esquemas de traducción ›  Traducción dirigida por la sintaxis en la práctica ›  Bibliografía
  6. 6. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 4 Introducción ¿Qué es la semán8ca de un lenguaje? Analizador léxico Analizador sintác8co Analizador semán8co Código intermedio Código final e · l · i · h · w <WHILE, PR> S WHILE E DO S E > E S WHILE E DO S E > E √ 0000 0011 0000 0011 0100 0001 0100 0000 0001 0010 … While ( a > b ) do a := a + 1; Etapa de análisis Etapa de síntesis LD a t1 LD b t2 GRT t3 t1 t2 BRZ t3 L1 … ¿Cómo es el lenguaje? Las dos primeras fases conceptuales de un compilador atienden a responder la pregunta de cómo es un lenguaje en términos de su estructura sintáctica y sus elementos léxicos constituyentes ¿Qué es el lenguaje? Las tres fases subsiguientes tratan de dar un significado único, preciso y computable a cada construcción del lenguaje de manera que el programador sepa definir programas semánticamente útiles y coherentes Tema 2 Tema 3 - 5 Tema 6 - 7 Tema 8 - 9 Tema 10 - 11
  7. 7. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 5 Introducción ¿Qué es la semán8ca de un lenguaje? PROGRAM BubbleSort; CONST MAX = 100; TYPE TVector = ARRAY [1..MAX] OF INTEGER; VAR v: TVector; PROCEDURE ReadVector (VAR v : TVector); ... PROCEDURE Sort (VAR v : TVector, size : INTEGER); VAR i, j, tmp : INTEGER; BEGIN FOR i := size - 1 DOWNTO 1 DO FOR j := 1 TO i DO IF (v[j] > v[j + 1]) THEN BEGIN tmp := v[j]; v[j] := v[j + 1]; v[j + 1] := tmp; END; END; BEGIN ReadVector (v); sort (v, MAX); END. El análisis sintáctico de un código fuente comprueba que la secuencia de tokens llegada desde el analizador léxico se corresponde con la esperada de acuerdo a las prescripciones gramaticales y se obtiene como resultado un árbol de análisis sintáctico. Hemos obtenido por tanto una secuencia correcta de tokens pero nada sabemos acerca de qué significado intencional subyace a la misma } Sabemos comprobar que este código fuente corresponde a un programa en Pascal de acuerdo a la gramática del lenguaje. Pero, ¿que significan la declaración de constantes, tipos, variables, funciones y procedimientos, las sentencias, expresiones, etc. que en él aparecen?
  8. 8. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 6 Introducción ¿Qué es la semán8ca de un lenguaje? Es necesario definir computacionalmente la semántica de un lenguaje para que el compilador sepa interpretar cada posible código fuente del mismo. La forma más sencilla de hacer esto es asociar a cada posible construcción gramatical ciertas reglas que permitan traducirla en términos computables. Esto sólo es posible si se cumple el principio de traducción dirigido por la sintaxis Traducción dirigida por la sintaxis El significado de una construcción de un lenguaje está directamente relacionado con su estructura sintáctica según se representa en su árbol de análisis Es decir, el significado de cada construcción gramatical, representada típicamente por un no terminal, solo debe depender de los elementos que aparecen en la parte derecha de sus reglas de producción La lectura práctica que debe hacerse de este principio es que para establecer la semántica de un lenguaje es suficiente con hacerlo sobre cada una de sus reglas de producción de manera independiente. Esto a su vez se consigue estableciendo una traducción de los elementos de la parte derecha de la regla a una representación con un significado computable específico DeclaraciónTipo ::= TYPE ID = ARRAY [N..N] OF Tipo;
  9. 9. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 7 Introducción ¿Qué es la semán8ca de un lenguaje? Es necesario definir computacionalmente la semántica de un lenguaje para que el compilador sepa interpretar cada posible código fuente del mismo. La forma más sencilla de hacer esto es asociar a cada posible construcción gramatical ciertas reglas que permitan traducirla en términos computables. Esto sólo es posible si se cumple el principio de traducción dirigido por la sintaxis Traducción dirigida por la sintaxis El significado de una construcción de un lenguaje está directamente relacionado con su estructura sintáctica según se representa en su árbol de análisis Las traducciones con significado computacional especifico a las que antes hacíamos referencia se pueden clasificar en 2 grandes grupos dependiendo del momento en que se aplican dentro del ciclo de compilación Tipos de acciones de traducción semán8ca Tiempo de compilación (semán8ca está8ca) Tiempo de ejecución (semán8ca dinámica) Las traducciones en tiempo de compilación son acciones que se aplican sobre los artefactos del compilador y que tienen efecto durante el proceso de compilación Las traducciones en tiempo de ejecución son acciones dirigidas a generar código ejecutable en tiempo de compilación para que se apliquen durante la ejecución del programa compilado } Tema 7 } Tema 8, 9
  10. 10. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 8 Introducción ¿Qué es la semán8ca de un lenguaje? CONST MAX = 100; R e g i s t r a r M A X c o m o constante entera de valor 100 Traducción semán8ca Construcción sintac8ca Tipo Ejemplo Tiempo de compilación Tiempo de ejecución Declaración de constantes TYPE TVector = ARRAY [1..MAX] OF INTEGER;Declaración de 6pos Registrar TVector como 6po ARRAY de tamaño 100 y base INTEGER VAR v : TVector;Declaración de variables Comprobar que TVector existe como un 6po y registrar v como una variable de ese 6po – – – PROCEDURE Sort (VAR v : TVector); Declaración de procedimientos Registrar Sort como un procedimiento del ámbito en curso indicando la lista de 6pos de los parámetros. Crear un nuevo ámbito y registrar en él v como variable de 6po TVector – Es necesario definir computacionalmente la semántica de un lenguaje para que el compilador sepa interpretar cada posible código fuente del mismo. La forma más sencilla de hacer esto es asociar a cada posible construcción gramatical ciertas reglas que permitan traducirla en términos computables. Esto sólo es posible si se cumple el principio de traducción dirigido por la sintaxis Traducción dirigida por la sintaxis
  11. 11. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 9 Introducción ¿Qué es la semán8ca de un lenguaje? Es necesario definir computacionalmente la semántica de un lenguaje para que el compilador sepa interpretar cada posible código fuente del mismo. La forma más sencilla de hacer esto es asociar a cada posible construcción gramatical ciertas reglas que permitan traducirla en términos computables. Esto sólo es posible si se cumple el principio de traducción dirigido por la sintaxis Traducción semán8ca Construcción sintac8ca Tipo Ejemplo Tiempo de compilación Tiempo de ejecución Function Sort (VAR v : TVector):INTEGER; Declaración de funciones Adicionalmente al caso de procedimientos registrar que el 6po de retorno es INTEGER – v[j] > v[j + 1] Expresiones Comprobar que v esta declarado previamente y que e s d e 6 p o T V e c t o r . Asegurarse de que las sub- expresiones son correctas y de 6pos compa6bles con el operador mayor que Generar código para recuperar de memoria el valor de las sub-expresiones y aplicar la comparación entre ambos con los operadores del lenguaje de bajo nivel Sentencia For FOR j := 1 TO i DO ... Comprobar que la expresión 1 e i son de 6po INTEGER. Comprobar que j ha sido previamente declarada como una variable de 6po INTEGER Generar código para inicializar j a 1. Generar código de salto para iterar la ejecución del bloque dentro de FOR i veces. Generar código para actualizar a cada paso el valor de j a j + 1 Traducción dirigida por la sintaxis
  12. 12. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 10 Introducción ¿Qué es la semán8ca de un lenguaje? Traducción semán8ca Construcción sintac8ca Tipo Ejemplo Tiempo de compilación Tiempo de ejecución Sentencia If IF (v[j] > v[j + 1]) THEN BEGIN tmp := v[j]; v[j] := v[j + 1]; v[j + 1] := tmp; END; Comprobar que la expresión d e c o m p a r a c i ó n e s semán6camente correcta (ver anterior) y comprobar que su evaluación devuelve un 6po lógico Generar código para evaluar la expresión durante la ejecución. Generar código de salto que ejecute el bloque THEN si la evaluación da TRUE en ejecución Sentencia de asignación tmp := v[j]; Comprobar que tmp esta definido. Comprobar que el 6po de base de v es compa6ble con el de tmp Generar código para evaluar el valor de v[j] y copiar su valor en la posición de memoria apuntada por tmp Sentencia de llamada a procedimiento sort (v, MAX); Comprobar que Sort esta declarado como procedi- miento y que el número, o r d e n y 6 p o d e l o s parámetros actuales y formales coinciden Generar código para ges6onar memoria que permita la ac6vación del p r o c e d i m i e n t o S o r t . Generar código de salto para su ejecución Es necesario definir computacionalmente la semántica de un lenguaje para que el compilador sepa interpretar cada posible código fuente del mismo. La forma más sencilla de hacer esto es asociar a cada posible construcción gramatical ciertas reglas que permitan traducirla en términos computables. Esto sólo es posible si se cumple el principio de traducción dirigido por la sintaxis Traducción dirigida por la sintaxis
  13. 13. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 11 Formalismos para la especificación de traducciones Especificación de traducciones dirigidas por la sintaxis Una vez entendido el proceso de traducción dirigida por la sintaxis que nos permite conferir semántica a las construcciones de un lenguaje, necesitamos mecanismos formales y tecnológicos para especificar dicha traducción de manera precisa Formalismos para la especificación de traducciones dirigidas por la sintaxis I. Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis son un mecanismo de alto nivel que permite expresar la semántica asociada a las construcciones de un lenguaje en términos de una colección de reglas semánticas asociadas a cada regla de producción gramatical II. Esquemas de traducción dirigidos por la sintaxis Los esquemas de traducción dirigidos por la sintaxis permiten insertar, entre los símbolos gramaticales de la parte derecha de las reglas de producción, acciones semánticas cuyo propósito es realizar cierta traducción de carácter semántico. La ventaja de esta aproximación es que aquí se prescribe el momento exacto de la compilación en el que cada acción es ejecutada n E E + E {:Œ:} n {:Œ:} {::} R1. E ::= E + E , Rs1 R2. E ::= n , Rs2
  14. 14. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 12 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Elementos de las definiciones dirigidas por la sintaxis declaracion ::= lVariables : tipo lVariables ::= lVariables1 , ID lVariables ::= ID tipo ::= INTEGER ; tipo ::= REAL ; lVariables.tipo := tipo.tipo lVariables1.tipo := lVariables.tipo <<registrar ID como variable de tipo tipo>> <<registrar ID como variable de tipo tipo>> tipo.tipo := “ENTERO” tipo.tipo := “REAL” I. Gramá8ca con atributos II. Reglas semán8cas A cada no terminal se asocian un conjunto de atributos para contener determinada información semántica que será utilizada en otras reglas de producción declaracion [] lVariables [tipo] tipo [tipo] Las reglas semánticas se encargan de transferir la información de unos atributos a otros y aplican acciones de traducción de carácter semántico lVariables1.tipo := lVariables.tipo <<registrar ID como variable de tipo tipo>> Reglas semánticasReglas de producción atributo Subíndice de diferenciación
  15. 15. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 13 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Atributos Un atributo es un espacio de información con tipificación implícita que sirve para albergar una característica propia del proceso de traducción y que puede ser transferida a otras producciones gramaticales para que pueda ser utilizada desde allí por sus reglas semánticas I. Atributos sinte8zados Se dice que un atributo es sintetizado si su valor es calculado exclusivamente a partir de atributos de los símbolos gramaticales de la parte derecha de sus reglas de producción. El carácter sintetizado de un atributo se mantiene a lo largo de toda la gramática 8pos de atributos II. Atributos heredados Se dice que un atributo es heredados si su valor es calculado a partir de atributos de los símbolos gramaticales de las regla de producción en que aparece o de los antecedentes de las misma. El carácter heredado de un atributo se mantiene a lo largo de toda la gramática A.a := f (B.b, C.c, D.D) A B C D b c d a B.b := f (A.a, C.c, D.D) A B C D b c d a
  16. 16. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 14 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Reglas semán8cas Una regla semántica es una expresión formal asociada a una regla de producción que permite definir la semántica de la construcción gramatical asociada a la misma en términos de los símbolos de la regla de producción I. Reglas de ecuaciones de atributos Las reglas de ecuaciones de atributos se utilizan para copiar, mover o transformar la información soportada por los atributos asociados a los símbolos de una regla hacia los atributos asociados a los símbolos no terminales de otra u otras reglas. Para concatenar valores de tipo cadena se usa el operador || 8pos de reglas semán8cas II. Reglas de acción semán8ca Las reglas de acción semántica son instrucciones o pseudo- insstrucciones que se utilizan para provocar algún efecto semántico sobre la traducción. Dado sus posibles efectos colaterales deben ser evitadas en la medida de lo posible. lVariables1.tipo := lVariables.tipo <<registrar ID como variable ...>> No terminal Terminal o No terminal asignación atributo
  17. 17. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 15 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Grafos de dependencias Un grafo de dependencias refleja, sobre un árbol de análisis sintáctico las dependencias entre los atributos gramaticales establecidas en virtud de las reglas semánticas que les asignan valor La aplicación de las ecuaciones de atributos de las reglas semánticas inducen ciertas dependencias entre los mismos. En efecto, si un atributo se calcula a partir de otros, es natural pensar que existe una dependencia del primero para con los segundos. Esta dependencia se dibuja en el grafo como un arco de cada uno de los segundos al primero B.b := f (A.a, C.c, D.D) A B C D b c d a El grafo de dependencias se utiliza para expresar gráficamente el orden en que deben ser calculados los atributos y por ende inducen un orden de aplicación de las regla semánticas. En efecto, si un atributo se calcula a partir de otros es natural pensar que antes de calcular el primeros necesario calcular los segundos Calcular A.a Calcular C.c Calcular D.d Calcular B.b en cualquier orden Calcular después
  18. 18. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 16 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Grafos de dependencias Un grafo de dependencias refleja, sobre un árbol de análisis sintáctico las dependencias entre los atributos gramaticales establecidas en virtud de las reglas semánticas que les asignan valor Algoritmo de cálculo Para cada producción A ::= X1 X2 ... Xn Crear un nodo por cada atributo Xi.aj de cada símbolo Para cada regla semántica de la forma Xi.aj = f (... , Xk.al, ...) Para cada k y cada l Crear arco desde Xk.al hasta Xi.aj Unir todos los grafos resultantes B.b := f (A.a, C.c, D.D) A B C D b c d a A.a := f (B.b, C.c, D.D) A B C D b c d a
  19. 19. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 17 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Grafos de dependencias a, b : INTEGER; declaracion ::= lVariables : tipo lVariables ::= lVariables1 , ID lVariables ::= ID tipo ::= INTEGER ; tipo ::= REAL ; lVariables.tipo := tipo.tipo lVariables1.tipo := lVariables.tipo <<registrar ID como variable de tipo tipo>> <<registrar ID como variable de tipo tipo>> tipo.tipo := “ENTERO” tipo.tipo := “REAL” declaración lVariables tipo INTEGERID : lVariables , ID .tipo .tipo .tipo “ENTERO” “ENTERO” “ENTERO” El grafo de dependencias pone de manifiesto que para registrar las variables a y b es necesario recuperar la información de tipo extraída de la regla tipo ::= INTEGER y moverla sobre el árbol de análisis sintáctico hasta las reglas con ID. Para ello se utiliza el atributo tipo en cada no terminal y las reglas semánticas adjuntas } lVariables.tipo [Heredado] tipo.tipo [sintetizado] <<registrar ID con tipo>> <<registrar ID con tipo>>
  20. 20. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 18 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Diseño de definiciones dirigidas por la sintaxis El diseño de definiciones dirigidas por la sintaxis es un ejercicio complejo que requiere realizarse cuidadosa y sistemáticamente. A continuación proporcionamos un procedimiento orientativo que puede ayudar a alcanzar más fácilmente este objetivo I. Especificación grama8cal Construya la gramática sobre la cual desea articular la definición dirigida por la sintaxis. Asegúrese de que esta gramática es correcta y no ambigua II. Selección de una frase representa8va Seleccione una frase del lenguaje. Asegúrese de que es lo suficientemente compleja como para que implique visitar al menos una vez cada regla gramatical III. Construcción del árbol de análisis Construya el árbol de análisis sintáctico asociado a dicha frase. Asegúrese de que existe al menos una instancia de cada regla IV. Selección de atributos Defina los atributos semánticos que requerirá su definición en cada símbolo gramatical y caracterícelos como sintetizados o heredados. Esto último se puede postergar para el final V. Grafo de dependencias Dibuje el grafo de dependencias que debería producirse para asegurar que la información fluya adecuadamente. Considere siempre un procesamiento descendente VI. Especificación de la definición Escriba las reglas semánticas necesarias , con el nivel de abstracción oportuno, para garantizar que se satisface el grafo de dependencias. Incluya las reglas de acción pertinentes
  21. 21. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 19 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Ejemplo Se pretende construir una calculadora que permita escribir expresiones aritméticas y calcular su valor asociado E ::= E + T | E – T | T T ::= T * F | T / F | F F ::= ( E ) | n ‘Œ 2 + 3 * 5  E T F E + FT * n n F T n Ž E.value [Sintetizado] T.value [Sintetizado] F.value [Sintetizado] n.value [Sintetizado]  E T F E + FT * n F T n  .value n .value .value .value .value .value .value .value 2 2 2 3 15 5 53 3 2 = 17 E ::= E + T E.value := E.value + T.value E ::= E – T E.value := E.value - T.value E ::= T E.value := T.value T ::= T * F T.value := T.value * F.value T ::= T / F T.value := T.value / F.value T ::= F T.value := F.value F ::= ( E ) F.value := E.value F ::= n F.value := n.value .value .value .value
  22. 22. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 20 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Ejercicios E ::= E + T | E – T | T T ::= T * F | T / F | F F ::= ( E ) | n A continuación se muestra una gramática de operadores no ambigua. Especifique sobre ella una DDS para pasar expresiones aritméticas infijas a notación postfija La siguiente gramática corresponde a la declaración de variables es Pascal. Especifique sobre ella una DDS para traducir dichas declaraciones a sintaxis C declaracion ::= lVariables : tipo lVariables ::= lVariables1 , ID lVariables ::= ID tipo ::= INTEGER ; tipo ::= REAL ; B ::= D B | D D ::= 0 | 1 La siguiente gramática representa números binarios. Defina una DDS que traduzca cualquier binario colocando todos los 0 delante y todos los 1 detrás. Por ejemplo 01101 se traduce a 00111 N ::= D N | D D ::= 0 | 1 | 2 | 3 | 4 5 | 6 | 7 La siguiente gramática se utiliza para representar números en octal. Constrúyase una DDS para obtener su traducción equivalente en base 10
  23. 23. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 21 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Gramá8cas con atributos por la izquierda En algunas ocasiones, las definiciones dirigidas por la sintaxis imponen un orden de evaluación de las reglas semánticas contrario al orden natural en el que el analizador sintáctico va consumiendo tokens de la cadena de entrada y construyendo el árbol de análisis sintáctico. En concreto el problema surge cuando aparecen dependencias que fluyen de derecha a izquierda dentro del grafo a, b : INTEGER; declaracion ::= lVariables : tipo lVariables ::= lVariables1 , ID lVariables ::= ID tipo ::= INTEGER ; tipo ::= REAL ; declaración lVariables tipo INTEGERID : lVariables , ID .tipo .tipo .tipo “ENTERO” “ENTERO” “ENTERO” <<registrar ID con tipo>> <<registrar ID con tipo>>
  24. 24. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 22 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Gramá8cas con atributos por la izquierda Este tipo de definiciones son conflictivas ya que las dependencias de derecha a izquierda exigen dejar pendiente el cálculo de un atributo y avanzar el procesamiento sintáctico hasta alcanzar la regla que calcula el atributo del que depende para posteriormente completar el primero en una segunda pasada. Esto obliga a hacer la siguiente clasificación Tipos de traductores I. Traductores de una sola pasada II. Traductores de doble pasada Los traductores sin dependencias de derecha a izquierda consiguen articular el cálculo de los atributos semánticos a medida que construyen el árbol de análisis sintáctico con lo que generan compiladores eficientes que recorren el árbol una sola vez Los traductores con dependencias de derecha a izquierda deben postergar el cálculo de algunos atributos hasta que se conozca el valor de todos aquellos de los que depende. Este tipo de compiladores deben construir el árbol de análisis sintáctico y realizar una segunda pasada para calcular los atributos pendientes declaración lVariables tipo: .tipo .tipo declaración tipo lVariables .tipo .tipo Gramática Pascal Gramática C L J
  25. 25. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 23 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Gramá8cas con atributos por la izquierda Los traductores de doble pasada empeoran notablemente el rendimiento del compilador. Por lo tanto es deseable que las definiciones dirigidas por la sintaxis no presenten dependencias de derecha a izquierda. De se así pueden aplicarse transformaciones gramaticales para intentar invertir la polaridad de las dependencias favorablemente declaracion ::= ID , declaracion1 declaracion.tipo = declaracion1.tipo <<registrar ID con tipo>> declaracion ::= ID : tipo declaracion.tipo = tipo.tipo <<registrar ID con tipo>> tipo ::= INTEGER ; tipo.tipo :=“ENTERO” tipo ::= REAL ; tipo.tipo := “REAL” declaración ID declaración, .tipo “ENTERO” “ENTERO” a, b : INTEGER; ID tipo: INTEGER <<registrar ID con tipo>> <<registrar ID con tipo>> .tipo “ENTERO” El árbol de análisis sintáctico ahora no fuerza la existencia de dependencias de derecha a izquierda puesto que para procesar la frase de declaración es prescriptivo alcanzar el tipo }
  26. 26. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 24 Definiciones dirigidas por la sintaxis Definiciones dirigidas por la sintaxis Las definiciones dirigidas por la sintaxis (DDS) son un formalismo de alto nivel que permite asociar a cada regla de producción gramatical una regla semántica descrita, principalmente, en términos de una transformación de ciertos atributos asociados a cada no terminal de la gramática. Gramá8cas con atributos por la izquierda Dados los problemas que presentan las dependencias de derecha a izquierda debemos buscar gramáticas que no induzcan este tipo ordenamiento semántico. A estas gramáticas se les conoce por el nombre de gramáticas con atributos por la izquierda Una gramática con atributos por la izquierda es una gramática de atributos en la que cada atributo heredado Xi.h de una regla de producción de la forma A ::= X1 … Xi … Xn, solo depende de los atributos X1 a Xi-1 o los atributos heredados de A A X1 X2 X3 La información fluye de arriba abajo, de de abajo a arriba y de izquierda a derecha pero nunca de derecha a izquierda }
  27. 27. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 25 Esquemas de traducción dirigidos por la sintaxis Esquemas de traducción dirigidos por la sintaxis Las definiciones dirigidas por la sintaxis son formalismos abstractos de traducción de alto nivel y por tanto adolecen de un mecanismo para indica el momento exacto en el que cada regla semántica debe ejecutarse. Una versión computacional que soluciona este problema son los esquemas de traducción dirigidos por la sintaxis Elementos de las traducciones dirigidas por la sintaxis I. Gramá8ca con atributos II. Acciones semán8cas Como en las definiciones dirigidas por la sintaxis, los esquemas de traducción también se construyen sobre gramáticas con atributos E [trad] E’ [trad] T [trad] num [lexema] Las acciones semánticas se insertan entre los símbolos gramaticales de la parte derecha de cada regla y, cuando se visitan durante la construcción del árbol de análisis sintáctico, realizan los traducciones semánticas pertinentes E ::= T {: E’.trad := T.trad :} E’ {: E.trad := E’.trad :} E’ ::= + T {: E’1.trad := E’.trad || T.trad || “+” :} E’1 {: E’.trad := E’1.trad :} E’ ::= - T {: E’1.trad := E’.trad || T.trad || “-” :} E’1 {: E’.trad := E’1.trad :} E’ ::= ε {: E’.trad := E’.trad :} T ::= num {: T.trad := num.lexema :} Reglas de producción Subíndice de diferenciación Acción semántica Atributo E ::= T {: E’.trad := T.trad :} E’ {: E.trad := E’.trad :}
  28. 28. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 26 Esquemas de traducción dirigidos por la sintaxis Esquemas de traducción dirigidos por la sintaxis Las definiciones dirigidas por la sintaxis son formalismos abstractos de traducción de alto nivel y por tanto adolecen de un mecanismo para indica el momento exacto en el que cada regla semántica debe ejecutarse. Una versión computacional que soluciona este problema son los esquemas de traducción dirigidos por la sintaxis Atributos Un atributo es un espacio de información con tipificación explícita que sirve para albergar una característica propia del proceso de traducción y que puede ser transferida a otras producciones gramaticales para que pueda ser utilizada desde allí por sus reglas semánticas I. Atributos sinte8zados Se dice que un atributo es sintetizado si su valor es calculado exclusivamente a partir de atributos de los símbolos gramaticales de la parte derecha de sus reglas de producción. El carácter sintetizado de un atributo se mantiene a lo largo de toda la gramática 8pos de atributos II. Atributos heredados Se dice que un atributo es heredados si su valor es calculado a partir de atributos de los símbolos gramaticales de las regla de producción en que aparece o de los antecedentes de las misma. El carácter heredado de un atributo se mantiene a lo largo de toda la gramática A.a := f (B.b, C.c, D.D) A B C D b c d a B.b := f (A.a, C.c, D.D) A B C D b c d a
  29. 29. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 27 Esquemas de traducción dirigidos por la sintaxis Esquemas de traducción dirigidos por la sintaxis Las definiciones dirigidas por la sintaxis son formalismos abstractos de traducción de alto nivel y por tanto adolecen de un mecanismo para indica el momento exacto en el que cada regla semántica debe ejecutarse. Una versión computacional que soluciona este problema son los esquemas de traducción dirigidos por la sintaxis Acción semán8ca Una acción semántica es un fragmento de código ejecutable, insertado en algún punto de la parte derecha de una regla de producción, encargado de realizar cierta lógica de traducción a partir de la información soportada por los atributos de los símbolos gramaticales de dicha regla E ::= T {: E’.trad := T.trad :} E’ {: E.trad := E’.trad :} Cada acción semántica se inserta en el lugar adecuado para que sea ejecutada en el momento preciso durante la construcción del árbol de análisis sintáctico } El contenido de la acción se expresa, frecuentemente, en el lenguaje de programación anfitrión del compilador que se está construyendo } Como en las DDS, las acciones semánticas frecuentemente r e a l i z a n o p e r a c i o n e s d e manipulación de atributos pero también puede llevar acabo operaciones con potenciales efectos colaterales }
  30. 30. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 28 Esquemas de traducción dirigidos por la sintaxis Esquemas de traducción dirigidos por la sintaxis Las definiciones dirigidas por la sintaxis son formalismos abstractos de traducción de alto nivel y por tanto adolecen de un mecanismo para indica el momento exacto en el que cada regla semántica debe ejecutarse. Una versión computacional que soluciona este problema son los esquemas de traducción dirigidos por la sintaxis Diseño de traducciones dirigidas por la sintaxis Antes de dar un procedimiento orientativo para la construcción de esquemas de traducción dirigidos por la sintaxis es preciso conocer ciertas restricciones de diseño de los mismos relacionadas con el lugar adecuado donde deben ubicarse las acciones semánticas }  Si existen atributos sintetizados colocar la acción semántica después de todos los símbolos implicados o al final de la producción }  Si existen atributos heredados deben calcularse antes de que aparezca el símbolo en la parte derecha }  Un atributo sintetizado no debe usarse antes de que aparezca el símbolo en la parte derecha A := B C {: A.a := f (B.a, C.a) :} D A:= B C D {: A.a := f (B.a, C.a) :} A := B C {: D.h := f (B.a, C.a) :} D A := B {: A.s := f (B.s, C.s) :} C D A := B C {: A.s := f (B.s, C.s) :} D }  Si existen acciones con efectos laterales deben situarse en el punto exacto de la parte derecha de la regla en la que deberían evaluarse. Hay que verificar que no utilizan atributos de símbolos situados a la derecha de dicho punto
  31. 31. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 29 Esquemas de traducción dirigidos por la sintaxis Esquemas de traducción dirigidos por la sintaxis Las definiciones dirigidas por la sintaxis son formalismos abstractos de traducción de alto nivel y por tanto adolecen de un mecanismo para indica el momento exacto en el que cada regla semántica debe ejecutarse. Una versión computacional que soluciona este problema son los esquemas de traducción dirigidos por la sintaxis Diseño de traducciones dirigidas por la sintaxis El diseño de esquemas de traducción dirigidos por la sintaxis es un ejercicio complejo que requiere realizarse cuidadosa y sistemáticamente. A continuación proporcionamos un procedimiento orientativo que puede ayudar a alcanzar más fácilmente este objetivo I. Especificación grama8cal Construya la gramática sobre la cual desea articular la traducción dirigida por la sintaxis. Asegúrese de que esta gramática es correcta y no ambigua II. Selección de una frase representa8va Seleccione una frase del lenguaje. Asegúrese de que es lo suficientemente compleja como para que implique visitar al menos una vez cada regla gramatical III. Construcción del árbol de análisis Construya el árbol de análisis sintáctico asociado a dicha frase. Asegúrese de que existe al menos una instancia de cada regla IV. Selección de atributos Defina los atributos que requerirá su esquema de traducción en cada símbolo gramatical y caracterícelos como sintetizados o heredados. Esto último se puede postergar para el final V. Grafo de dependencias Dibuje el grafo de dependencias que debería producirse para asegurar que la información fluya adecuadamente. Considere siempre un procesamiento descendente VI. Especificación de la traducción Escriba la traducción dirigida por la sintaxis ubicando las acciones semánticas de acuerdo a las restricciones de diseño preliminares
  32. 32. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 30 Esquemas de traducción dirigidos por la sintaxis Esquemas de traducción dirigidos por la sintaxis Las definiciones dirigidas por la sintaxis son formalismos abstractos de traducción de alto nivel y por tanto adolecen de un mecanismo para indica el momento exacto en el que cada regla semántica debe ejecutarse. Una versión computacional que soluciona este problema son los esquemas de traducción dirigidos por la sintaxis Ejemplo E T E’{: E’.trad := ‘9’ :} {: E.trad := ‘9 5 – 2 +’ :} num (9) {: T.trad := ‘9’ :} - T E’{: E’1.trad := ‘9 5 -’ :} {: E’.trad := ‘9 5 – 2 +’ :} num (5) {: T.trad := ‘5’ :} + T E’{: E’1.trad := ‘9 5 – 2 +’ :} {: E’.trad := ‘9 5 – 2 +’ :} num (2) {: T.trad := ‘2’ :} ε {: E’.trad := ‘9 5 – 2 +’ :}
  33. 33. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 31 Traducción dirigida por la sintaxis en la prác6ca Esquemas de traducción en la prác8ca El siguiente paso para construir el compilador es articular en cup un esquema de traducción dirigido por la sintaxis. Esta herramienta ya provee un mecanismo para llevar esto a cabo. A continuación damos la secuencia de pasos que deben realizarse Implementación de clases No Terminales Consúltese el documento directrices de implementación La implementación de esquemas de traducción en cup parte de la implementación de una clase para cada no terminal. Estas clases son contenedores para los atributos semánticos que serán utilizados posteriormente I. Construcción de la clase Para implementar la clase asociada a cada no terminal recomendamos que se utilice el mismo nombre que se usó para nombrarlo en cup, eliminando caracteres extraños y siguiendo los criterios de nombrado de Java. Si lo cree conveniente créese un script para automatizar la construcción de estas clases con un esqueleto vacío. Además considere la posibilidad de extender de una clase base para factorizar cierto código común a todas las clases de no terminales Todas estas clases deben implementarse en el paquete nonTerminal public Expresion extends NonTerminal { ... }
  34. 34. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 32 Traducción dirigida por la sintaxis en la prác6ca Esquemas de traducción en la prác8ca El siguiente paso para construir el compilador es articular en cup un esquema de traducción dirigido por la sintaxis. Esta herramienta ya provee un mecanismo para llevar esto a cabo. A continuación damos la secuencia de pasos que deben realizarse Implementación de clases No Terminales Consúltese el documento directrices de implementación La implementación de esquemas de traducción en cup parte de la implementación de una clase para cada no terminal. Estas clases son contenedores para los atributos semánticos que serán utilizados posteriormente II. Implementación de constructores Incluya en cada clase tantos constructores como resulten necesarios. Recomendamos definir un constructor por defecto sin argumentos y otros que reciban como argumentos otras clases de no terminales necesarias, de acuerdo al análisis de dependencias del esquema de traducción dirigido por la sintaxis Considere la posibilidad de centralizar la lógica común de cada constructor en uno de los constructores de manera que el resto delegue en ellos public Expresion extends NonTerminal { ... public Expresion () { ... } public Expresion (Expresion e) {...} public Expresion (Expresion left, Expresion right) {...} }
  35. 35. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 33 Traducción dirigida por la sintaxis en la prác6ca Esquemas de traducción en la prác8ca El siguiente paso para construir el compilador es articular en cup un esquema de traducción dirigido por la sintaxis. Esta herramienta ya provee un mecanismo para llevar esto a cabo. A continuación damos la secuencia de pasos que deben realizarse Implementación de clases No Terminales Consúltese el documento directrices de implementación La implementación de esquemas de traducción en cup parte de la implementación de una clase para cada no terminal. Estas clases son contenedores para los atributos semánticos que serán utilizados posteriormente III. Implementación atributos Declare como variables privadas de clase cada uno de los atributos que contendrá el no terminal de la clase que está implementando. Para cada atributo implemente dos métodos de acceso público. una operación consultora con prefijo get y otra modificadora con prefijo set. Considere esta tarea como un proceso de refinamiento sucesivo que se irá completando cuando tenga claro qué atributos es necesario incluir en cada clase no terminal a tenor del esquema de traducción que está articulando public Expresion extends NonTerminal { List<QuadrupleIF> code; TypeIF type; TemporalIF temporal; ... public List<QuadrupleIF> getCode () {...} public void setCode (List<QuadrupleIF> code) {...} public TypeIF getType() {...} public void setType (TypeIF type) {...} public TemporalIF getTemporal () {...} public void setTemporal (TemporalIF temporal) {...} ... }
  36. 36. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 34 Traducción dirigida por la sintaxis en la prác6ca Esquemas de traducción en la prác8ca El siguiente paso para construir el compilador es articular en cup un esquema de traducción dirigido por la sintaxis. Esta herramienta ya provee un mecanismo para llevar esto a cabo. A continuación damos la secuencia de pasos que deben realizarse Implementación de clases No Terminales Consúltese el documento directrices de implementación La implementación de esquemas de traducción en cup parte de la implementación de una clase para cada no terminal. Estas clases son contenedores para los atributos semánticos que serán utilizados posteriormente IV. Sobrescritura de métodos de Object Java impone, normativamente, que la imple- mentación de cualquier clase debe incluir la sobreescritura de 3 métodos: hashcode, para devolver una representación numérica única del objeto; equals, para comparar un objeto con otro y toString para devolver una cadena que contenga inforrmación sobre el estado del problema. Recomendamos encarecidamente la implementación de estos tres métodos puesto que puede simplificar considerablemente la construcción del compiladores en fases subsiguientes public Expresion extends NonTerminal { ... long hashcode () { return 67 * 67 * code.hashcode() + 67 * type.hashcode() + temporal.hascode(); } boolean equals (Object o) { if (!(o instanceof Expresion)) return false; else { Expression e = (Expresion) o; return e.code.equals(this.code) && e.type.equals(this.type) && e.temporal.equals(this.temporal); } } public String toString () {...} }
  37. 37. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 35 Traducción dirigida por la sintaxis en la prác6ca Esquemas de traducción en la prác8ca El siguiente paso para construir el compilador es articular en cup un esquema de traducción dirigido por la sintaxis. Esta herramienta ya provee un mecanismo para llevar esto a cabo. A continuación damos la secuencia de pasos que deben realizarse Tipificación de No Terminales en cup Consúltese el documento directrices de implementación Una vez construidas todas las clases de los no terminales que conforman nuestra especificación gramatical en cup, es necesario indicar a esta herramienta que, durante el proceso de construcción del árbol de análisis sintáctico, debe considerar cada no terminal como un objeto de la clase correspondiente. Esto se hace alterando la sección de declaración de no terminales mediante su tipificación Se incluye el nombre de la clase entre nonTerminal y el nombre del no terminal. Asegúrese de haber importado nonTerminal
  38. 38. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 36 Traducción dirigida por la sintaxis en la prác6ca Esquemas de traducción en la prác8ca El siguiente paso para construir el compilador es articular en cup un esquema de traducción dirigido por la sintaxis. Esta herramienta ya provee un mecanismo para llevar esto a cabo. A continuación damos la secuencia de pasos que deben realizarse Implementación del esquema de traducción en cup Consúltese el documento directrices de implementación En cup, la implementación de esquemas de traducción dirigidos por la sintaxis permite utilizar solamente atributos sintetizados. Cada regla de producción debe tener una acción semántica que construya un objeto de la clase no terminal del antecedente, asigne valor a sus atributos, y, como última instrucción, lo asigne a RESULT. Esto hará que dicho objeto se copie al no terminal del antecedente para que sea consultado en otras reglas expresion ::= expresion:e1 MAS expresion:e2 {: Expresion e = new Expresion (e1, e2); <<comprobación de tipos sobre e1 y e2>> <<generación de código para e1 + e2>> ... RESULT = e; :} Uso de constructor Acciones semánticas Asignación de reducción Al hacer esta asignación el objeto en la expresión de la parte derecha del igual se asigna como contenedor de información del no terminal del antecedente. Asegúrese de que el tipo de esta expresión es compatible con el declarado en la sección de no terminales para el antecedente Nombramiento de símbolos gramaticales
  39. 39. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 37 Traducción dirigida por la sintaxis en la prác6ca 1.  Implementación de las clases java que representan los no terminales 1.  Hacerlos heredar de la clase NonTerminal 2.  Implementar constructores y constructores por copia 3.  Sobrescribir métodos heredados de la clase Object 4.  Considere la posibilidad de automatizar el proceso con script 2.  Tipificación de cada no terminal en Cup 1.  Declare el tipo – clase java – de cada no terminal de cup 2.  Considere la posibilidad de hacer jerarquías de clases 3.  Pruebe a implementar algún esquema de traducción sencillo de prueba 1.  Implemente una prueba para comprobar la propagación de información en cup 2.  Tenga en cuenta siempre que cada acción introducida semánticas pueden generar nuevos conflictos Los atributos de los no terminales aún no son conocidos por lo tanto es imposible implementar sus métodos de acceso Desarrollo paso a paso La construcción de esta fase del compilador consiste en la preparación de la infraestructura de artefactos necesaria para articular los esquemas de traducción dirigida por la sintaxis tal y como se ha descrito con anterioridad. A continuación damos un generación de los pasos principales a dar En Cup A ::= B {: <<acción>> :} C se traduce a A ::= B M C; M ::= {:<<acción>>:} lo cual puede llegar a generar nuevos conflictos en ocasiones
  40. 40. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 38 Bibliograna Material de estudio Bibliografía básica Construcción de compiladores: principios y práctica Kenneth C. Louden International Thomson Editores, 2004 ISBN 970-686-299-4
  41. 41. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semán8co. Traducción dirigida por la sintaxis 6 - 39 Bibliograna Material de estudio Bibliografía complementaria Compiladores: Principios, técnicas y herramientas. Segunda Edición Aho, Lam, Sethi, Ullman Addison – Wesley, Pearson Educación, México 2008 Diseño de compiladores. A. Garrido, J. Iñesta, F. Moreno y J. Pérez. 2002. Edita Universidad de Alicante
  42. 42. Procesadores de Lenguajes Ingeniería Técnica superior de Ingeniería Informá8ca Departamento de Lenguajes y Sistemas informá6cos Javier Vélez Reyes jvelez@lsi.uned.es Departamento de Lenguajes Y Sistemas InformáAcos UNED 7 Análisis semán*co II Comprobación de Apos
  43. 43. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 2 Obje6vos Obje8vos ›  Aprender qué es la comprobación de tipos ›  Entender qué es un sistema de tipos ›  Conocer los elementos constituyentes de un sistema de tipos ›  Conocer qué es una expresión de tipos ›  Aprender a escribir expresiones de tipos ›  Aprender las diferencias entre comprobación estática y dinámica ›  Entender qué es la equivalencia de tipos ›  Aprender las diferencias entre los distintos tipos de equivalencia de tipos ›  Entender qué es y como funciona la conversión de tipos ›  Entender qué es la sobrecarga y qué tipos de sobrecarga existen ›  Aprender a implementar un comprobador de tipos ›  Conocer las principales estructuras de datos y artefactos necesarios
  44. 44. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 3 Índice Índice ›  Introducción ›  Símbolos ›  Tipos ›  Ámbitos de declaración ›  El sistema de tipos ›  ¿Qué es el sistema de tipos? ›  Tipos primitivos ›  Constructores de tipos ›  Equivalencia de tipos ›  Verificación de tipos sobre operadores ›  Verificación de tipos sobre subprogramas ›  Construcción de un comprobador de tipos ›  Qué es un comprobador de tipos ›  Artefactos de un comprobador de tipos ›  Apertura de ámbito inicial ›  Declaración de constantes ›  Declaración de tipos ›  Declaración de variables ›  Declaración de subprogramas ›  Expresiones ›  Sentencias ›  Cierre de ámbitos ›  Temas avanzados de un comprobador de tipos ›  Inferencia de tipos ›  Sobrecarga de subprogramas ›  Recuperación de errores semánticos ›  Desarrollo paso a paso ›  Bibliografía
  45. 45. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 4 Introducción Análisis semán8co La fase de análisis semántico tiene por objetivo analizar los componentes del árbol de análisis sintáctico que representan el programa con el fin de comprobar que se respetan ciertas reglas semánticas que dan un significado coherente a las construcciones del lenguaje El analizador semántico va comprobando la coherencia semántica del árbol de análisis sintáctico según se va construyendo Foco de atención Las responsabilidades son… ›  Registrar declaraciones ›  Inferir tipos ›  Comprobar tipos ›  Comprobar corrección semántica Analizador semán8co SWhile WHILE E DO S E > E SWhile WHILE E DO S E > E ü ü ü ü ü
  46. 46. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 5 Introducción Conceptos esenciales Los lenguajes modernos se articulan a partir de una estructura de bloques donde se pueden hacer declaraciones que luego serán referenciadas a lo largo del programa CONST MAX = 10; TYPE TVector = ARRAY [1..10] OF REAL; VAR v, w : TVector; i : INTEGER; PROCEDURE Leer (VAR m: TVector); VAR i : INTEGER; FUNCTION Validar (VAR v: INTEGER): INTEGER; BEGIN Validar := v mod 10; END; BEGIN FOR i := 1 TO 10 DO m [i] := Validar (Read (i)); END; END; FUNCTION suma (m, n: TVector) : TVector; VAR i : INTEGER; vSuma : TVector; BEGIN WHILE (i < MAX) DO vSuma [i] := m[i] + n[i]; INC (i); END; suma := vSuma; END; BEGIN Leer (v); Leer (w); v := suma (v, w); END. Símbolos Un símbolo de un programa es cualquier elemento con nombre que el programador haya establecido como válido dentro de él a través de una declaración El ejercicio de creación de un programa consiste en la declaración de una colección de símbolos con nombres que representan entidades semánticas utilizadas a lo largo del código El tipo de símbolos que pueden declararse en un p r o g r a m a e s u n a característica propia del lenguaje. No obstante puede identificarse ciertos tipos de símbolos recurrentes tales como constantes, variables, funciones, procedimientos o parámetros Algunos lenguajes permiten elidir la declaración de los símbolos y utilizan reglas de inferencia para determinar los símbolos que son utilizados a lo largo del programa
  47. 47. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 6 Introducción Conceptos esenciales Los lenguajes modernos se articulan a partir de una estructura de bloques donde se pueden hacer declaraciones que luego serán referenciadas a lo largo del programa Tipos Un tipo es un descriptor semántico que permite cualificar al símbolo al que está vinculado de manera que condiciona su interpretación semántica y restringe las operaciones que se pueden realizar sobre él Cada símbolo del lenguaje lleva asociado un tipo. El tipo de un símbolo se utiliza para proporcionar información al compilador de cómo debe tratar semánticamente al mismo y qué operaciones debe permitir realizar sobre él CONST MAX = 10; TYPE TVector = ARRAY [1..10] OF REAL; VAR v, w : TVector; i : INTEGER; PROCEDURE Leer (VAR m: TVector); VAR i : INTEGER; FUNCTION Validar (VAR v: INTEGER): INTEGER; BEGIN Validar := v mod 10; END; BEGIN FOR i := 1 TO 10 DO m [i] := Validar (Read (i)); END; END; FUNCTION suma (m, n: TVector) : TVector; VAR i : INTEGER; vSuma : TVector; BEGIN WHILE (i < MAX) DO vSuma [i] := m[i] + n[i]; INC (i); END; suma := vSuma; END; BEGIN Leer (v); Leer (w); v := suma (v, w); END. Todo lenguaje dispone de una colección de tipos primitivos y t a m b i é n d e c i e r t o s mecanismos semánticos para construir nuevos tipos a partir d e o t r o s p r e v i a m e n t e definidos En aquellos lenguajes donde la tipificación no es explícita los símbolos adquieren un tipo tan pronto como sea posible aplicando reglas de inferencia de tipos
  48. 48. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 7 Introducción Conceptos esenciales Los lenguajes modernos se articulan a partir de una estructura de bloques donde se pueden hacer declaraciones que luego serán referenciadas a lo largo del programa Ámbitos de declaración y alcance de visibilidad Un ámbito es un bloque acotado sintácticamente dentro del cual los símbolos allí declarados tienen vigencia Cada símbolo se declara dentro de un bloque acotado por ciertas construcciones sintácticas. Se entiende que dentro de dicho bloque la declaración tiene vigencia y puede ser utilizada mientras que fuera no existe y pueden declararse otros símbolos incluso aunque compartan el mismo nombre En lenguajes estilo Pascal las construcciones sintácticas que representan creaciones de nuevos ámbitos, a parte del global, son únicamente la declaración de funciones y procedimientos En lenguajes estilo C, además de las funciones y el ámbito global, cualquier bloque de sentencias acotado entre llaves constituye un ámbito de declaración nuevo CONST MAX = 10; TYPE TVector = ARRAY [1..10] OF REAL; VAR v, w : TVector; i : INTEGER; PROCEDURE Leer (VAR m: TVector); VAR i : INTEGER; FUNCTION Validar (VAR v: INTEGER): INTEGER; BEGIN Validar := v mod 10; END; BEGIN FOR i := 1 TO 10 DO m [i] := Validar (Read (i)); END; END; FUNCTION suma (m, n: TVector) : TVector; VAR i : INTEGER; vSuma : TVector; BEGIN WHILE (i < MAX) DO vSuma [i] := m[i] + n[i]; INC (i); END; suma := vSuma; END; BEGIN Leer (v); Leer (w); v := suma (v, w); END.
  49. 49. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 8 Introducción Conceptos esenciales Los lenguajes de programación articulan la definición de programas a través de la declaración de símbolos convenientemente tipificados que luego son referenciados a lo largo del código Anidamiento de ámbitos Dadas varias declaraciones diferentes para el mismo nombre en distintos ámbitos anidados, la declaración que se aplica a una referencia es aquélla que se encuentre en el bloque anidado más próximo a la misma Cuando dos ámbitos se encuentran sintácticamente anidados uno dentro de otro, desde el más interno tienen visibilidad todas las declaraciones de símbolos realizadas en el ámbito más externo. Si dentro del ámbito interno se declara un nuevo símbolo con el mismo nombre que un símbolo en el ámbito externo, la declaración que pasa a tener vigencia es la realizada dentro del ámbito interno. Esto es cierto para cualquier nivel de anidamiento CONST MAX = 10; TYPE TVector = ARRAY [1..10] OF REAL; VAR v, w : TVector; i : INTEGER; PROCEDURE Leer (VAR m: TVector); VAR i : INTEGER; FUNCTION Validar (VAR v: INTEGER): INTEGER; BEGIN Validar := v mod 10; END; BEGIN FOR i := 1 TO 10 DO m [i] := Validar (Read (i)); END; END; FUNCTION suma (m, n: TVector) : TVector; VAR i : INTEGER; vSuma : TVector; BEGIN WHILE (i < MAX) DO vSuma [i] := m[i] + n[i]; INC (i); END; suma := vSuma; END; BEGIN Leer (v); Leer (w); v := suma (v, w); END. Regla de anidamiento más cercano ámbitonivel0 ámbitonivel1ámbitonivel1 ámbitonivel2
  50. 50. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 9 Introducción ¿Qué es la comprobación de 8pos? La labor de comprobación de tipos consiste en conferir a las construcciones sintácticas del lenguaje la semántica de tipificación que acabamos de describir y en realizar todo tipo de comprobaciones de dicha índole. Por su naturaleza, sin embargo, ésta se encuentra repartida entre la fase de análisis semántico y la generación de código intermedio Análisis semán8co Generación código intermedio S WHILE E DO S E > E S WHILE E DO S E > E √ LD a t1 LD b t2 GRT t3 t1 t2 BRZ t3 L1 … I. Comprobaciones está8cas II. Comprobaciones dinámicas Las comprobaciones estáticas recogen el compendio de todas aquellas tareas de carácter semántico que, por su naturaleza, pueden ser realizadas directamente durante la fase de compilación mediante el uso de los artefactos y mecanismos propios de dicha fase. Este tipo de comprobaciones son beneficiosas puesto que confieren seguridad a la ejecución del programa Las comprobaciones dinámicas son aquellas que no se realizan durante la fase de compilación y se delegan al momento de la ejecución del programa. Ello requiere generar código ejecutable específicamente diseñado para realizar tales comprobaciones. Los lenguajes con una carga excesiva de comprobaciones dinámicas generan programas más largos, lentos e inseguros en ejecución Tipos de comprobaciones semán8cas Tema 7 } Tema 8-9 }
  51. 51. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 10 Introducción Tipos de comprobaciones semán8cas está8cas I. Ges8ón de declaraciones III. Inferencia de 8pos Se encarga de registrar todas las declaraciones realizadas por el programador a lo largo de los distintos ámbitos. Esta tarea implica el registro de tipos y la comprobación de que no se produce ninguna colisión de nombres con los identificadores de otras declaraciones En lenguajes sin tipificación de variables o con sobrecarga se aplican tareas de inferencia de tipos en el nivel gramatical de las expresiones para resolver el tipo de datos de la expresión resultante en función del contexto de evaluación II. Verificación de 8pos Comprueba la compatibilidad de tipos de todas las expresiones del código fuente recuperando la información durante la gestión de declaraciones. Además se asegura de que no existe en el programa ninguna referencia a ningún símbolo no declarado ¿Qué es la comprobación de 8pos? La labor de comprobación de tipos consiste en conferir a las construcciones sintácticas del lenguaje la semántica de tipificación que acabamos de describir y en realizar todo tipo de comprobaciones de dicha índole. Por su naturaleza, sin embargo, ésta se encuentra repartida entre la fase de análisis semántico y la generación de código intermedio CONST MAX = 10; TYPE TVector = ARRAY [1..10] OF REAL; VAR v, w : TVector; i : INTEGER; PROCEDURE Leer (VAR m: TVector); Validar := v mod 10; WHILE (i < MAX) DO ... v := suma (v, w); CONST MAX = 10;
  52. 52. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 11 Sistemas de 6pos ¿Qué es el sistema de 8pos? El documento que recoge toda la información relativa a la tipificación de un lenguaje de programación, desde sus tipos primitivos, hasta sus tipos estructurados pasando por sus reglas de combinación operacional, recibe el nombre de sistema de tipos El sistema de tipos de un lenguaje es una especificación de alto nivel que describe de forma precisa el conjunto de reglas y restricciones semánticas de tipificación que se aplican sobre las construcciones sintácticas del lenguaje Niveles de 8pificación El sistema de tipos de un lenguaje condiciona, de manera directa, la seguridad de los programas en tiempo de ejecución. En efecto, cuantas más comprobaciones se hagan en tiempo de compilación, menor será la probabilidad de fallo en ejecución. El nivel de construcción del sistema de tipos permite clasificar a los lenguajes en 2 categorías Niveles de 8pificación Lenguajes fuertemente 8pificados Lenguajes débilmente 8pificados La tipificación fuerte implica un nivel de construcción elevado y es propia de lenguajes de programación seguros como Pascal, Ada o Modula La tipificación débil implica pocas restricciones en el sistema de tipos y delega la mayor parte de la comprobación al tiempo de ejecución. Es propia de lenguajes de scripting como Groovy o Javascript
  53. 53. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 12 Sistemas de 6pos ¿Qué es el sistema de 8pos? El documento que recoge toda la información relativa a la tipificación de un lenguaje de programación, desde sus tipos primitivos, hasta sus tipos estructurados pasando por sus reglas de combinación operacional, recibe el nombre de sistema de tipos Expresiones de 8pos Para definir el sistema de tipos de un lenguaje de manera formal, sistemática e independientemente de las sintaxis propia del mismo se utilizan expresiones de tipos. Cada tipo primitivo tiene una expresión de tipo asociada. Cada constructor de tipo tiene también una expresión de tipo. Cada tipo definido mediante la composición de constructores de tipos y tipos primitivos tiene a su vez una expresión de tipos Una expresión de tipos es un mecanismo formal utilizado por los sistemas de tipos para representar el tipo de una construcción sintáctica propia del lenguaje El sistema de tipos de un lenguaje es una especificación de alto nivel que describe de forma precisa el conjunto de reglas y restricciones semánticas de tipificación que se aplican sobre las construcciones sintácticas del lenguaje
  54. 54. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 13 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Tipos primi8vos Expresión de 8pos Tipo Byte Integer Word Real Double Boolean Char String Representación numérica corta sin signo Representación numérica con signo Representación numérica larga sin signo Representación flotante corta con signo Representación flotante larga con signo Representación lógica Representación de carácter Representación de cadena Descripción Rango 0 a 255 -32768 a 32767 0 a 65535 2.9E-39 a 1.7E38 5.0E-324 a 1.7E308 False, True ASCII - ENTERO ENTERO ENTERO REAL REAL LOGICO CARÁCTER CADENA Operaciones + – * / mod = > < <> + – * / mod = > < <> + – * / mod = > < <> + – * / = > < <> + – * / = > < <> AND OR XOR NOT - - Los tipos primitivos de un lenguaje determinan la colección de tipos de datos originales que el lenguaje pone a disposición del programador para componer estructuras de datos más complejas. La variedad de tipos primitivas es una característica propia del lenguaje pero en general se distinguen cuatro categorías: ordinales, reales, lógicos y de carácter
  55. 55. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 14 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Constructores de 8pos Expresión de 8pos Ejemplo TVector = ARRAY [1..10] OF INTEGER Representa una formación lineal de elementos de un mismo 6po de base Descripción ARRAY (1..10, ENTERO) Los constructores de tipos son mecanismos sintácticos del lenguaje que permiten combinar otras construcciones de tipos, primitivos o compuestos, para generar estructuras más complejas. Los tipos así generados se llaman tipos complejos, tipos compuestos o tipos definidos por el usuario TPunto = RECORD BEGIN X: INTEGER; Y: INTEGER END; Representa una estructura de datos cons6tuida por una colección de campos con nombre de dis6nto 6po RECORD ( (X x ENTERO) x (Y x ENTERO) ) ^ TPunto Representa un puntero a un área de memoria reservada para almacenar datos de un determinado 6po de base POINTER (TPunto) TConjunto = SET OF INTEGER Representa un conjunto no ordenado de elemento de un mismo 6pos de datos de base SET (ENTERO)
  56. 56. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 15 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Constructores de 8pos Expresión de 8pos Ejemplo FUNCTION suma (m, n: TVector): Tvector; Representa una declaración de función con parámetros y 6po de retorno definido Descripción (Tvector x TVector) " TVector Los constructores de tipos son mecanismos sintácticos del lenguaje que permiten combinar otras construcciones de tipos, primitivos o compuestos, para generar estructuras más complejas. Los tipos así generados se llaman tipos complejos, tipos compuestos o tipos definidos por el usuario PROCEDURE Leer ( n: TVector); Representa una declaración de procedimiento con parámetros definidos (Tvector) " TVector (INTEGER; INTEGER) Representa una tupla de datos cons6tuida por una colección ordenada de datos de dis6nto 6po (ENTERO) x (ENTERO) TPalos = (OROS, COPAS, ESPADAS, BASTOS); Representa una colección de e6quetas que se comportan como constantes simbólicas de valor numérico ENUM (OROS, COPAS, ESPADAS, BASTOS)
  57. 57. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 16 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Constructores de 8pos Los constructores de tipos son mecanismos sintácticos del lenguaje que permiten combinar otras construcciones de tipos, primitivos o compuestos, para generar estructuras más complejas. Los tipos así generados se llaman tipos complejos, tipos compuestos o tipos definidos por el usuario Ejercicios Una vez que se conoce la expresión de tipos de los principales tipos del lenguaje y de los constructores de tipos pueden construirse las expresiones de tipos para diferentes tipos definidos por el usuario TPEntero = ^ INTEGER; TMatriz = ARRAY [1..3][1..6]; TPersona = RECORD BEGIN nombre: STRING; edad : INTEGER; END; FUNCTION mayor (a, b: INTEGER): INTEGER; FUNCTION ordenar (p: TPEntero) : TPEntero; TLista = RECORD BEGIN vector: ARRAY [1..10] OF INTEGER; longitud: INTEGER; END; TTabla = ARRAY [1..100] OF ^TLista; TCjtoTablas = SET OF ^TTabla;
  58. 58. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 17 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Equivalencia de 8pos De cara a realizar comprobaciones estáticas, resulta interesante definir la equivalencia entre dos tipos complejos del lenguaje. Esta definición puede formularse en términos del nombre que reciben sendos tipos dentro del código o entre las expresiones de tipos subyacentes. Esto permite distinguir entre dos formas de entender la equivalencia lo cual es una característica intrínseca del lenguaje I. Equivalencia nominal TYPE T1 = ARRAY [1..10] OF INTEGER; T2 = ARRAY [1..10] OF INTEGER; T3 = T1 Se dice que dos tipos de datos T1 y T2 son nominalmente equivalentes si responden a una misma entrada dentro del registro de tipos realizado por el compilador durante la gestión de declaraciones T1 T2 T3 Equivalente No equivalente Equivalente No equivalente Equivalente No Equivalente Equivalente No equivalente Equivalente T1 T2 T3
  59. 59. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 18 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Equivalencia de 8pos De cara a realizar comprobaciones estáticas, resulta interesante definir la equivalencia entre dos tipos complejos del lenguaje. Esta definición puede formularse en términos del nombre que reciben sendos tipos dentro del código o entre las expresiones de tipos subyacentes. Esto permite distinguir entre dos formas de entender la equivalencia lo cual es una característica intrínseca del lenguaje II. Equivalencia estructural TYPE T1 = ARRAY [1..10] OF INTEGER; T2 = ARRAY [1..10] OF INTEGER; T3 = T1 Se dice que dos tipos de datos son estructuralmente equivalentes si son el mismo tipo básico o están formadas mediante la aplicación del mismo constructor de tipos sobre expresiones de tipos estructuralmente equivalentes T1 T2 T3 Equivalente Equivalente Equivalente Equivalente Equivalente Equivalente Equivalente Equivalente Equivalente T1 T2 T3
  60. 60. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 19 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Equivalencia de 8pos De cara a realizar comprobaciones estáticas, resulta interesante definir la equivalencia entre dos tipos complejos del lenguaje. Esta definición puede formularse en términos del nombre que reciben sendos tipos dentro del código o entre las expresiones de tipos subyacentes. Esto permite distinguir entre dos formas de entender la equivalencia lo cual es una característica intrínseca del lenguaje II. Equivalencia estructural boolean equivale (TypeIF s, TypeIF t) { if (s == t) return true; if ((s == ARRAY (s1, s2) && t == ARRAY (t1,t2)) || (s == (s1 x s2) && t == (t1 x t2)) || (s == (s1 " s2) && t == (t1 " t2))) return equivale (s1,t1) && equivale (t2, t2); if ((s == Pointer (s1) && t == Pointer (t1)) || (s == Set (s1) && t == Set (t1)) return equivale (s1,t1); return false } Se dice que dos tipos de datos son estructuralmente equivalentes si son el mismo tipo básico o están formadas mediante la aplicación del mismo constructor de tipos sobre expresiones de tipos estructuralmente equivalentes Ojo con las definiciones de tipos recursivos }
  61. 61. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 20 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Verificación de 8pos sobre operadores La mayoría de operadores de un lenguaje de programación pueden operarse satisfactoriamente sobre un subconjunto de tipos primitivos del lenguaje. Esto desencadena una serie de conceptos tan interrelacionados entre si que conviene abordarlos conjuntamente I. Sobrecarga de operadores 2 + 3 4.2 + 3.8 En estos ejemplos en Pascal puede verse como el sistema de tipos del lenguaje, define el operador suma de forma sobrecargada ya que puede ser evaluado en el contexto de 2 subexpresiones de tipo entero o tipo flotante, entre otros. Otros lenguajes, como Fortran utilizan por el contrario operadores diferentes S e d i c e q u e u n o p e r a d o r e s t á sobrecargado cuando se puede utilizar para operar sobre un subconjunto de tipos de datos primitivos del lenguaje 2 + 3 4.2 +. 3.8 } Pascal Fortran La capacidad de poder utilizar un mismo operador para articular diferentes operaciones en función de los tipos de datos involucrados se llama sobrecarga de operadores. Es frecuente sobrecargar los operadores aritméticos y relacionales de un lenguaje
  62. 62. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 21 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Verificación de 8pos sobre operadores La mayoría de operadores de un lenguaje de programación pueden operarse satisfactoriamente sobre un subconjunto de tipos primitivos del lenguaje. Esto desencadena una serie de conceptos tan interrelacionados entre si que conviene abordarlos conjuntamente II. Compa8bilidad de 8pos 2 + 3.5 4.2 * 3 En los ejemplos en Pascal puede verse como los operadores sobrecargados suma (+) y producto (*) definen los tipos entero y real como compatibles entre sí, mientras que en C resultan compatibles el tipo carácter, entero y flotante con respecto a los mismos operadores Se dice que dos tipos son compatibles entre si, con respecto a un operador, si son equivalentes o si se pueden operar satisfactoriamente a través de dicho operador ‘a’ + 3.5 4.2 * 3 } Pascal C La capacidad de sobrecarga de los operadores de un lenguaje, introduce el concepto de compatibilidad de tipos, que se aplica cuando dichos tipos pueden ser satisfactoriamente operados a través de un operador sobrecargado
  63. 63. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 22 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Verificación de 8pos sobre operadores La mayoría de operadores de un lenguaje de programación pueden operarse satisfactoriamente sobre un subconjunto de tipos primitivos del lenguaje. Esto desencadena una serie de conceptos tan interrelacionados entre si que conviene abordarlos conjuntamente III. Coerción de 8pos 2 + 3.5 4.2 * 3 En los ejemplos de Pascal ambas operaciones operan entre reales y por tanto los operandos enteros se convierten a real. Igual ocurre con la primera expresión en C que convierte el carácter ‘a’ a entero y de ahí a flotante para poder operarse. Estas conversiones se llaman implícitas. Sin embargo la ultima fuerza a convertir el flotante a entero con perdida de información. Esta conversión es explícita La coerción de tipos es el proceso mediante el cual el sistema de tipos convierte la subexpresión menos restrictiva hacia la más restrictiva cuando ambas son de distinto tipo ‘a’ + 3.5 (int) 4.2 * 3 } Pascal C La compatibilidad de tipos permite operar expresiones de tipos diferentes pero comúnmente fuerza conversiones de tipo hacia el tipo más restrictivo. Este efecto recibe el nombre de conversión o coerción de tipos
  64. 64. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 23 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Verificación de 8pos sobre operadores La mayoría de operadores de un lenguaje de programación pueden operarse satisfactoriamente sobre un subconjunto de tipos primitivos del lenguaje. Esto desencadena una serie de conceptos tan interrelacionados entre si que conviene abordarlos conjuntamente Byte Integer Word Real Double Error Error Error Byte Integer Word Real Double Boolean Char String ... Byte Integer Word Real Double Boolean Char String … Integer Integer Word Real Double Error Error Error Word Word Word Real Double Error Error Error Real Real Real Real Double Error Error Error Double Double Double Double Double Error Error Error Error Error Error Error Error Error Error Error + Error Error Error Error Error Error Error Error Error Error Error Error Error Error Error Error operador tipos Coerción o error Matriz de compatibilidad para el operador + Constructores de tipos
  65. 65. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 24 Sistemas de 6pos Elementos de un sistema de 8pos La descripción del sistema de tipos de un lenguaje de programación se articula, fundamentalmente, mediante la definición de los tipos primitivos y constructores compuestos, sus reglas de composición, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan Verificación de 8pos sobre subprogramas La sobrecarga es un término que puede ser igualmente aplicado sobre los procedimientos y funciones declarados en un programa. Esto permite soportar diferentes implementaciones de un mismo subprograma con igual identificador pero diferente, número, tipo u orden de parámetros formales en su declaración. La implementación invocada dependerá de los tipos aplicados en la expresión de llamada El procedimiento de suma está sobrecargado. La sobrecarga de subprogramas es más propia de lenguajes orientados a objetos que de lenguajes estructurados. En ese contexto se llama polimorfismo La sobrecarga de subprogramas es la capacidad de un lenguaje de permitir la declaración de varios procedimientos o funciones con el mismo nombre pero distinto, número, tipo u orden de parámetros formales TYPE TVector = ARRAY [1..10] OF INTEGER TMatriz = ARRAY [1..10][1..5] OF INTEGER VAR v, w: Tvector; m, n : TMatriz; PROCEDURE sumar (v, w: TVector; VAR r:TVector); ... PROCEDURE sumar (m, n: TMatriz; VAR r:TMatriz); ... }
  66. 66. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 25 Construcción de comprobadores de 6pos en la prác6ca ¿Qué es un comprobador de 8pos? Una vez descritas las responsabilidades y características de un sistema de tipos, es preciso implementarlo en la practica a través de los mecanismos proporcionados por los esquemas de traducción dirigidos por la sintaxis. A la parte de un compilador que implementa el sistema de tipos se le llama comprobador de tipos Un comprobador de tipos es la parte del compilador que se encarga de implementar el sistema de tipos del lenguaje a través de mecanismos de traducción dirigida por la sintaxis expresion ::= expresion:e1 MAS expresion:e2 {: t1 = <<recuperar tipo de e1>> t2 = <<recuperar tipo de e2>> <<Comprobar la compatibilidad de t1 y t2 para +>> No: <<Error semántico>> si: <<otras acciones semánticas>> :} Ejemplo En el caso de las expresiones es necesario recuperar el tipo de cada subexpresión y comprobar su compatibilidad con respecto al operador que las combina. Si no son compatibles se emite un mensaje de error semántico }
  67. 67. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 26 Construcción de comprobadores de 6pos en la prác6ca Artefactos de un comprobador de 8pos Antes de comenzar con la descripción de la implementación del comprobador de tipos para cada parte de la gramática de un lenguaje es necesario presentar la colección de artefactos que serán utilizados dentro de la misma. A continuación describimos aquellos que aparecen en el framework de soporte ScopeManager SymbolTable Scope TypeTable Consúltese el documento directrices de implementación SymbolIF TypeIF SymbolBase TypeBase SymbolConstant SymbolVariable SymbolFunction SymbolProcedure SymbolParameter TypeSimple TypeRecord TypeUnion TypeArray TypeEnum TypeSet TypePointer TypeProcedure TypeFunction SemanticErrorManager
  68. 68. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 27 Construcción de comprobadores de 6pos en la prác6ca Artefactos de un comprobador de 8pos Antes de comenzar con la descripción de la implementación del comprobador de tipos para cada parte de la gramática de un lenguaje es necesario presentar la colección de artefactos que serán utilizados dentro de la misma. A continuación describimos aquellos que aparecen en el framework de soporte Consúltese el documento directrices de implementación Tipos primi8vos y constructores de 8pos Las clases que implementan el interfaz TypeIF representan los tipos primitivos del lenguaje y cada uno de los constructores de tipos del mismo. El primer paso es determinar los atributos y métodos necesarios para caracterizarlos (entendemos que los nombres de cada clase son autoexplicativos) TypeBase - String name - ScopeIF scope + String getName () + void setName (String name) + ScopeIF getScope () + void setScope (ScopeIF scope) + int getSize() + boolean equals () + hashcode () + toString () Todos los tipos contienen un nombre y un ámbito de declaración por tanto pueden factorizarse en la clase abstracta TypeBase e implementar sendos métodos de consulta y modificación Propiedad name y scope Equivalencia de 8pos Para mantener limpio el código del esquema de traducción en Cup se recomienda implementar la equivalencia de tipos – nominal o estructural – en este método Sobrescritura de métodos de Object Igualmente se recomienda la implementación de los métodos hashcode y toString heredados de clase Object. Consulte tema 6 para obtener detalles
  69. 69. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 28 Construcción de comprobadores de 6pos en la prác6ca Artefactos de un comprobador de 8pos Antes de comenzar con la descripción de la implementación del comprobador de tipos para cada parte de la gramática de un lenguaje es necesario presentar la colección de artefactos que serán utilizados dentro de la misma. A continuación describimos aquellos que aparecen en el framework de soporte Consúltese el documento directrices de implementación Tipos primi8vos y constructores de 8pos Las clases que implementan el interfaz TypeIF representan los tipos primitivos del lenguaje y cada uno de los constructores de tipos del mismo. El primer paso es determinar los atributos y métodos necesarios para caracterizarlos (entendemos que los nombres de cada clase son autoexplicativos) TypeSimple TypeRecord TypeUnion TypeEnum TypeSet TypePointer TypeProcedure - String name - ScopeIF scope - int size TypeArray - String name - ScopeIF scope - int min - int max - TypeIF base - String name - Map <String, TypeIF> fields - ScopeIF scope - String name - TypeIF base - ScopeIF scope - String name - List <String> values - ScopeIF scope - String name - ScopeIF scope - TypeIF base - String name - List <TypeIF> parameters - ScopeIF scope TypeFunction - TypeIF rType - ScopeIF scope - String name - Map <String, TypeIF> baseFields - Map <String, Map<String, TypeIF>> variants - ScopeIF scope
  70. 70. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 29 Construcción de comprobadores de 6pos en la prác6ca Artefactos de un comprobador de 8pos Antes de comenzar con la descripción de la implementación del comprobador de tipos para cada parte de la gramática de un lenguaje es necesario presentar la colección de artefactos que serán utilizados dentro de la misma. A continuación describimos aquellos que aparecen en el framework de soporte Consúltese el documento directrices de implementación Símbolos Las clases que implementan la interfaz SymbolIF representan símbolos que han sido declarados por el usuario programador dentro del lenguaje. Para cada tipo de símbolo también conviene identificar los atributos propios que lo caracterizan de manera preliminar SymbolBase - String name - TypeIF type - ScopeIF scope + String getName () + void setName (String name) + ScopeIF getScope () + void setScope (ScopeIF scope) + TypeIF getType () + void setType (TypeIF type) + boolean equals () + hashcode () + toString () Todos los símbolos contienen un nombre y un ámbito de declaración por tanto pueden factorizarse en la clase abstracta SymbolBase e implementar sendos métodos de consulta y modificación Propiedad name y scope Propiedad Tipo De forma similar, todos los símbolos declarados tienen un tipo, ya sea declarado explícitamente o inferido luego el tipo es otra propiedad que puede factorizarse en esta clase Sobrescritura de métodos de Object Igualmente se recomienda la implementación de los métodos hashcode y toString heredados de clase Object. Consulte tema 6 para obtener detalles
  71. 71. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 30 Construcción de comprobadores de 6pos en la prác6ca Artefactos de un comprobador de 8pos Antes de comenzar con la descripción de la implementación del comprobador de tipos para cada parte de la gramática de un lenguaje es necesario presentar la colección de artefactos que serán utilizados dentro de la misma. A continuación describimos aquellos que aparecen en el framework de soporte Consúltese el documento directrices de implementación Símbolos Las clases que implementan la interfaz SymbolIF representan símbolos que han sido declarados por el usuario programador dentro del lenguaje. Para cada tipo de símbolo también conviene identificar los atributos propios que lo caracterizan de manera preliminar SymbolProcedure SymbolConstant SymbolParameter SymbolVariable - String name - TypeIF type - ScopeIF scope - Integer address - String name - TypeIF type - ScopeIF scope - String name - TypeIF type - ScopeIF scope - String name - TypeIF type - Number value - ScopeIF scope SymbolFunction - String name - TypeIF Type - ScopeIF scope Al igual que en el caso de los tipos, estas definiciones serán potencialmente extendidas cuando avancemos en la construcción del compilador }
  72. 72. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 31 Construcción de comprobadores de 6pos en la prác6ca Artefactos de un comprobador de 8pos Antes de comenzar con la descripción de la implementación del comprobador de tipos para cada parte de la gramática de un lenguaje es necesario presentar la colección de artefactos que serán utilizados dentro de la misma. A continuación describimos aquellos que aparecen en el framework de soporte Consúltese el documento directrices de implementación Gestor de errores semán8cos y trazabilidad Cuando se detecta un error semántico durante el proceso de compilación, el comprobador de tipos debe emitir un mensaje de error. El gestor de errores proporciona métodos para informar de errores recuperables y no recuperables así como para trazar la ejecución del comprobador semántico SemanticErrorManager + void semantiDebug (String message) + void semanticInfo (String message) + void semanticWarn (String message) + void semanticError (String message) + void semanticFatal (String message) exp ::= exp:e1 MAS exp:e2 {: TypeIF t1 = <<obtener tipo e1>> TypeIF t2 = <<obtener tipo e2>> semanticErrorManager.semanticDebug (“Tipo de e1:” + t1); semanticErrorManager.semanticDebug (“Tipo de e2:” + t2); if (t1.isCompatible (t2, TypeIF.MAS)) { ... } else semanticErrorManager.semanticFatalError (“tipos incompatibles”); :}
  73. 73. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 32 Construcción de comprobadores de 6pos en la prác6ca Artefactos de un comprobador de 8pos Antes de comenzar con la descripción de la implementación del comprobador de tipos para cada parte de la gramática de un lenguaje es necesario presentar la colección de artefactos que serán utilizados dentro de la misma. A continuación describimos aquellos que aparecen en el framework de soporte Consúltese el documento directrices de implementación Tablas de 8pos Una tabla de tipos es una estructura de datos de tabla hash donde se registran todas las declaraciones de tipos realizadas por el usuario programador en un determinado ámbito TypeTable Private ScopeIF scope; private Map <String, TypeIF> tTable; public TypeTable (ScopeIF scope) { this.scope = scope; tTable = new HashMap<String, TypeIF> (); } public TypeIF getType (String id) { return tTable.get (id); } public void addType (TypeIF type) { String id = type.getName (); addType (id, type); } TypeTable (continúa) public void addType (String id, TypeIF type){ type.setScope (this.scope); tTable.put (id, type); } public boolean containsType (String id) { return tTable.containsKey (id); } public boolean containsType (TypeIF type) { String id = type.getName (); return containsType (id); } ...
  74. 74. Javier Vélez Reyes jvelez@lsi.uned.es Análisis semántico. Comprobación de tipos 7 - 33 Construcción de comprobadores de 6pos en la prác6ca Artefactos de un comprobador de 8pos Antes de comenzar con la descripción de la implementación del comprobador de tipos para cada parte de la gramática de un lenguaje es necesario presentar la colección de artefactos que serán utilizados dentro de la misma. A continuación describimos aquellos que aparecen en el framework de soporte Consúltese el documento directrices de implementación Tablas de símbolos Una tabla de símbolos es una estructura de datos de tabla hash donde se registran todas las declaraciones (no tipos) realizadas por el usuario programador en un determinado ámbito SymbolTable Private ScopeIF scope; private Map <String, SymbolIF> sTable; public SymbolTable() { sTable = new HashMap<String, SymbolIF> (); } public SymbolIF getSymbol (String id) { return sTable.get (id); } public void addSymbol (SymbolIF symbol) { String id = symbol.getName (); addSymbol (id, symbol); } SymbolTable(continúa) public void addSymbol (String id, SymbolIF symbol){ symbol.setScope (this.scope); sTable.put (id, symbol); } public boolean containsSymbol (String id) { return sTable.containsKey (id); } public boolean containsSymbol (SymbolIF symbol) { String id = symbol.getName (); return containsSymbol (id); } ...

×