Sunteți pe pagina 1din 54

Procesadores de Lenguajes

Ingeniera Tcnica superior de Ingeniera Informtica


Departamento de Lenguajes y Sistemas informticos

Anlisis semntico II
Comprobacin de tipos
Javier Vlez Reyes jvelez@lsi.uned.es Departamento de Lenguajes Y Sistemas Informticos UNED

Anlisis semntico. Comprobacin de tipos


Objetivos

Objetivos
Aprender qu es la comprobacin de tipos Entender qu es un sistema de tipos Conocer los elementos constituyentes de un sistema de tipos Conocer qu es una expresin de tipos Aprender a escribir expresiones de tipos Aprender las diferencias entre comprobacin esttica y dinmica 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 conversin 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

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


ndice

ndice
Introduccin Smbolos Tipos mbitos de declaracin El sistema de tipos Qu es el sistema de tipos? Tipos primitivos Constructores de tipos Equivalencia de tipos Verificacin de tipos sobre operadores Verificacin de tipos sobre subprogramas Construccin de un comprobador de tipos Qu es un comprobador de tipos Artefactos de un comprobador de tipos Apertura de mbito inicial Declaracin de constantes Declaracin de tipos Declaracin de variables Declaracin de subprogramas Expresiones Sentencias Cierre de mbitos Temas avanzados de un comprobador de tipos Inferencia de tipos Sobrecarga de subprogramas Recuperacin de errores semnticos Desarrollo paso a paso Bibliografa
Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Introduccin

Anlisis semntico
La fase de anlisis semntico tiene por objetivo analizar los componentes del rbol de anlisis sintctico que representan el programa con el fin de comprobar que se respetan ciertas reglas semnticas que dan un significado coherente a las construcciones del lenguaje SWhile Las responsabilidades son WHILE E Foco de atencin El analizador semntico va comprobando la coherencia semntica del rbol de anlisis sintctico segn se va construyendo SWhile WHILE E E E > DO E S Registrar declaraciones Inferir tipos Comprobar tipos Comprobar correccin semntica

Analizador semntico

DO E

>

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Introduccin

Conceptos esenciales
Los lenguajes modernos se articulan a partir de una estructura de bloques donde se pueden hacer declaraciones que luego sern 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.

Smbolos
El ejercicio de creacin de un programa consiste en la declaracin de una coleccin de smbolos con nombres que representan entidades semnticas utilizadas a lo largo del cdigo Un smbolo de un programa es cualquier elemento con nombre que el programador haya establecido como vlido dentro de l a travs de una declaracin El tipo de smbolos que pueden declararse en un programa es una caracterstica propia del lenguaje. No obstante puede identificarse ciertos tipos de smbolos recurrentes tales como constantes, variables, funciones, procedimientos o parmetros Algunos lenguajes permiten elidir la declaracin de los smbolos y utilizan reglas de inferencia para determinar los smbolos que son utilizados a lo largo del programa

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Introduccin

Conceptos esenciales
Los lenguajes modernos se articulan a partir de una estructura de bloques donde se pueden hacer declaraciones que luego sern 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.

Tipos
Cada smbolo del lenguaje lleva asociado un tipo. El tipo de un smbolo se utiliza para proporcionar informacin al compilador de cmo debe tratar semnticamente al mismo y qu operaciones debe permitir realizar sobre l Un tipo es un descriptor semntico que permite cualificar al smbolo al que est vinculado de manera que condiciona su interpretacin semntica y restringe las operaciones que se pueden realizar sobre l Todo lenguaje dispone de una coleccin de tipos primitivos y tambin de ciertos mecanismos semnticos para construir nuevos tipos a partir de otros previamente definidos En aquellos lenguajes donde la tipificacin no es explcita los smbolos adquieren un tipo tan pronto como sea posible aplicando reglas de inferencia de tipos

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Introduccin

Conceptos esenciales
Los lenguajes modernos se articulan a partir de una estructura de bloques donde se pueden hacer declaraciones que luego sern 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.

mbitos de declaracin y alcance de visibilidad


Cada smbolo se declara dentro de un bloque acotado por ciertas construcciones sintcticas. Se entiende que dentro de dicho bloque la declaracin tiene vigencia y puede ser utilizada mientras que fuera no existe y pueden declararse otros smbolos incluso aunque compartan el mismo nombre Un mbito es un bloque acotado sintcticamente dentro del cual los smbolos all declarados tienen vigencia En lenguajes estilo Pascal las construcciones sintcticas que representan creaciones de nuevos mbitos, a parte del global, son nicamente la declaracin de funciones y procedimientos En lenguajes estilo C, adems de las funciones y el mbito global, cualquier bloque de sentencias acotado entre llaves constituye un mbito de declaracin nuevo

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Introduccin

Conceptos esenciales
Los lenguajes de programacin articulan la definicin de programas a travs de la declaracin de smbolos convenientemente tipificados que luego son referenciados a lo largo del cdigo
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.

Anidamiento de mbitos
Cuando dos mbitos se encuentran sintcticamente anidados uno dentro de otro, desde el ms interno tienen visibilidad todas las declaraciones de smbolos realizadas en el mbito ms externo. Si dentro del mbito interno se declara un nuevo smbolo con el mismo nombre que un smbolo en el mbito externo, la declaracin que pasa a tener vigencia es la realizada dentro del mbito interno. Esto es cierto para cualquier nivel de anidamiento

Dadas varias declaraciones diferentes para el mismo nombre en distintos mbitos anidados, la declaracin que se aplica a una referencia es aqulla que se encuentre en el bloque anidado ms prximo a la misma
mbito nivel 0

mbito nivel 1

Regla de anidamiento ms cercano

mbito nivel 1

Javier Vlez Reyes jvelez@lsi.uned.es

mbito nivel 2

Anlisis semntico. Comprobacin de tipos


Introduccin

Qu es la comprobacin de tipos?
La labor de comprobacin de tipos consiste en conferir a las construcciones sintcticas del lenguaje la semntica de tipificacin 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 anlisis semntico y la generacin de cdigo intermedio

Tipos de comprobaciones semnticas


I. Comprobaciones estticas
Las comprobaciones estticas recogen el compendio de todas aquellas tareas de carcter semntico que, por su naturaleza, pueden ser realizadas directamente durante la fase de compilacin mediante el uso de los artefactos y mecanismos propios de dicha fase. Este tipo de comprobaciones son beneficiosas puesto que confieren seguridad a la ejecucin del programa

S WHILE E DO S

Las comprobaciones dinmicas son aquellas que no se realizan durante la fase de compilacin y se delegan al momento de la ejecucin del programa. Ello requiere generar cdigo ejecutable especficamente diseado para realizar tales comprobaciones. Los lenguajes con una carga excesiva de comprobaciones dinmicas generan programas ms largos, lentos e inseguros en ejecucin

II. Comprobaciones dinmicas

Tema 7

E > E

Anlisis semntico

WHILE

S E DO S

Tema 8-9

E > E

Generacin cdigo intermedio


LD a t1 LD b t2 GRT t3 t1 t2 BRZ t3 L1

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Introduccin

Qu es la comprobacin de tipos?
La labor de comprobacin de tipos consiste en conferir a las construcciones sintcticas del lenguaje la semntica de tipificacin 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 anlisis semntico y la generacin de cdigo intermedio

Tipos de comprobaciones semnticas estticas


I. Gestin de declaraciones
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 comprobacin de que no se produce ninguna colisin de nombres con los identificadores de otras declaraciones
CONST MAX = 10; TYPE TVector = ARRAY [1..10] OF REAL; VAR v, w : TVector; i : INTEGER; PROCEDURE Leer (VAR m: TVector);

II. Verificacin de tipos


Comprueba la compatibilidad de tipos de todas las expresiones del cdigo fuente recuperando la informacin durante la gestin de declaraciones. Adems se asegura de que no existe en el programa ninguna referencia a ningn smbolo no declarado
Validar := v mod 10; WHILE (i < MAX) DO ... v := suma (v, w);

III. Inferencia de tipos


En lenguajes sin tipificacin 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 expresin resultante en funcin del contexto de evaluacin
CONST MAX = 10;

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Qu es el sistema de tipos?
El documento que recoge toda la informacin relativa a la tipificacin de un lenguaje de programacin, desde sus tipos primitivos, hasta sus tipos estructurados pasando por sus reglas de combinacin operacional, recibe el nombre de sistema de tipos El sistema de tipos de un lenguaje es una especificacin de alto nivel que describe de forma precisa el conjunto de reglas y restricciones semnticas de tipificacin que se aplican sobre las construcciones sintcticas del lenguaje

Niveles de tipificacin
El sistema de tipos de un lenguaje condiciona, de manera directa, la seguridad de los programas en tiempo de ejecucin. En efecto, cuantas ms comprobaciones se hagan en tiempo de compilacin, menor ser la probabilidad de fallo en ejecucin. El nivel de construccin del sistema de tipos permite clasificar a los lenguajes en 2 categoras Lenguajes fuertemente tipificados
La tipificacin fuerte implica un nivel de construccin elevado y es propia de lenguajes de programacin seguros como Pascal, Ada o Modula

Niveles de tipificacin

Lenguajes dbilmente tipificados


La tipificacin dbil implica pocas restricciones en el sistema de tipos y delega la mayor parte de la comprobacin al tiempo de ejecucin. Es propia de lenguajes de scripting como Groovy o Javascript

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Qu es el sistema de tipos?
El documento que recoge toda la informacin relativa a la tipificacin de un lenguaje de programacin, desde sus tipos primitivos, hasta sus tipos estructurados pasando por sus reglas de combinacin operacional, recibe el nombre de sistema de tipos El sistema de tipos de un lenguaje es una especificacin de alto nivel que describe de forma precisa el conjunto de reglas y restricciones semnticas de tipificacin que se aplican sobre las construcciones sintcticas del lenguaje

Expresiones de tipos
Para definir el sistema de tipos de un lenguaje de manera formal, sistemtica e independientemente de las sintaxis propia del mismo se utilizan expresiones de tipos. Cada tipo primitivo tiene una expresin de tipo asociada. Cada constructor de tipo tiene tambin una expresin de tipo. Cada tipo definido mediante la composicin de constructores de tipos y tipos primitivos tiene a su vez una expresin de tipos Una expresin de tipos es un mecanismo formal utilizado por los sistemas de tipos para representar el tipo de una construccin sintctica propia del lenguaje

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Tipos primitivos
Los tipos primitivos de un lenguaje determinan la coleccin de tipos de datos originales que el lenguaje pone a disposicin del programador para componer estructuras de datos ms complejas. La variedad de tipos primitivas es una caracterstica propia del lenguaje pero en general se distinguen cuatro categoras: ordinales, reales, lgicos y de carcter Tipo
Byte Integer Word Real Double Boolean Char String

Descripcin

Rango

Expresin de tipos
ENTERO ENTERO ENTERO REAL REAL LOGICO CARCTER CADENA

Operaciones
+ * / mod = > < <> + * / mod = > < <> + * / mod = > < <> + * / = > < <> + * / = > < <> AND OR XOR NOT -

Representacin numrica corta sin signo 0 a 255 Representacin numrica con signo Representacin flotante corta con signo Representacin flotante larga con signo Representacin lgica Representacin de carcter Representacin de cadena -32768 a 32767 2.9E-39 a 1.7E38 5.0E-324 a 1.7E308 False, True ASCII Representacin numrica larga sin signo 0 a 65535

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Constructores de tipos
Los constructores de tipos son mecanismos sintcticos del lenguaje que permiten combinar otras construcciones de tipos, primitivos o compuestos, para generar estructuras ms complejas. Los tipos as generados se llaman tipos complejos, tipos compuestos o tipos definidos por el usuario Ejemplo
TVector = ARRAY [1..10] OF INTEGER TPunto = RECORD BEGIN X: INTEGER; Y: INTEGER END; ^ TPunto

Descripcin
Representa una formacin lineal de elementos de un mismo tipo de base Representa una estructura de datos constituida por una coleccin de campos con nombre de distinto tipo Representa un puntero a un rea de memoria reservada para almacenar datos de un determinado tipo de base Representa un conjunto no ordenado de elemento de un mismo tipos de datos de base

Expresin de tipos
ARRAY (1..10, ENTERO) RECORD ( (X x ENTERO) x (Y x ENTERO) )

POINTER (TPunto)

TConjunto = SET OF INTEGER

SET (ENTERO)

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Constructores de tipos
Los constructores de tipos son mecanismos sintcticos del lenguaje que permiten combinar otras construcciones de tipos, primitivos o compuestos, para generar estructuras ms complejas. Los tipos as generados se llaman tipos complejos, tipos compuestos o tipos definidos por el usuario Ejemplo
TPalos = (OROS, COPAS, ESPADAS, BASTOS); (INTEGER; INTEGER)

Descripcin
Representa una coleccin de etiquetas que se comportan como constantes simblicas de valor numrico Representa una tupla de datos constituida por una coleccin ordenada de datos de distinto tipo Representa una declaracin de funcin con parmetros y tipo de retorno definido Representa una declaracin de procedimiento con parmetros definidos

Expresin de tipos
ENUM (OROS, COPAS, ESPADAS, BASTOS)

(ENTERO) x (ENTERO)

FUNCTION suma (m, n: TVector): Tvector; PROCEDURE Leer ( n: TVector);

(Tvector x TVector) TVector

(Tvector) TVector

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Constructores de tipos
Los constructores de tipos son mecanismos sintcticos del lenguaje que permiten combinar otras construcciones de tipos, primitivos o compuestos, para generar estructuras ms complejas. Los tipos as generados se llaman tipos complejos, tipos compuestos o tipos definidos por el usuario

Ejercicios
Una vez que se conoce la expresin 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
TLista = RECORD BEGIN vector: ARRAY [1..10] OF INTEGER; longitud: INTEGER; END; TTabla = ARRAY [1..100] OF ^TLista; TCjtoTablas = SET OF ^TTabla; 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;

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Equivalencia de tipos
De cara a realizar comprobaciones estticas, resulta interesante definir la equivalencia entre dos tipos complejos del lenguaje. Esta definicin puede formularse en trminos del nombre que reciben sendos tipos dentro del cdigo o entre las expresiones de tipos subyacentes. Esto permite distinguir entre dos formas de entender la equivalencia lo cual es una caracterstica intrnseca del lenguaje

I. Equivalencia nominal
TYPE

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 gestin de declaraciones
T1 T2 T3

T1 = ARRAY [1..10] OF INTEGER; T2 = ARRAY [1..10] OF INTEGER; T3 = T1

T1 Equivalente Equivalente

T2

T3

No equivalente Equivalente No equivalente No Equivalente Equivalente

No equivalente Equivalente

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Equivalencia de tipos
De cara a realizar comprobaciones estticas, resulta interesante definir la equivalencia entre dos tipos complejos del lenguaje. Esta definicin puede formularse en trminos del nombre que reciben sendos tipos dentro del cdigo o entre las expresiones de tipos subyacentes. Esto permite distinguir entre dos formas de entender la equivalencia lo cual es una caracterstica intrnseca del lenguaje

II. Equivalencia estructural


TYPE

Se dice que dos tipos de datos son estructuralmente equivalentes si son el mismo tipo bsico o estn formadas mediante la aplicacin del mismo
T1 T2 T3

T1 = ARRAY [1..10] OF INTEGER; T2 = ARRAY [1..10] OF INTEGER; T3 = T1

T1 Equivalente Equivalente Equivalente

T2 Equivalente Equivalente Equivalente

T3 Equivalente Equivalente Equivalente

constructor de tipos sobre expresiones de tipos estructuralmente equivalentes

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Equivalencia de tipos
De cara a realizar comprobaciones estticas, resulta interesante definir la equivalencia entre dos tipos complejos del lenguaje. Esta definicin puede formularse en trminos del nombre que reciben sendos tipos dentro del cdigo o entre las expresiones de tipos subyacentes. Esto permite distinguir entre dos formas de entender la equivalencia lo cual es una caracterstica intrnseca del lenguaje

II. Equivalencia estructural


Se dice que dos tipos de datos son estructuralmente equivalentes si son el mismo tipo bsico o estn formadas mediante la aplicacin del mismo
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 }

constructor de tipos sobre expresiones de tipos estructuralmente equivalentes


Ojo con las definiciones de tipos recursivos

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Verificacin de tipos sobre operadores


La mayora de operadores de un lenguaje de programacin 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
La capacidad de poder utilizar un mismo operador para articular diferentes operaciones en funcin de los tipos de datos involucrados se llama sobrecarga de operadores. Es frecuente sobrecargar los operadores aritmticos y relacionales de un lenguaje Se dice que un operador est Pascal
2 + 3 4.2 + 3.8

Fortran
2 + 3 4.2 +. 3.8

sobrecargado cuando se puede utilizar para operar sobre un subconjunto de tipos de datos primitivos del lenguaje

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

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Verificacin de tipos sobre operadores


La mayora de operadores de un lenguaje de programacin 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. Compatibilidad de tipos


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 travs de un operador sobrecargado Se dice que dos tipos son compatibles entre si, con respecto a un operador, si son equivalentes o si a se pueden de operar dicho Pascal
2 + 3.5 4.2 * 3

C
a + 3.5 4.2 * 3

satisfactoriamente operador

travs

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 carcter, entero y flotante con respecto a los mismos operadores

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Verificacin de tipos sobre operadores


La mayora de operadores de un lenguaje de programacin 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. Coercin de tipos


La compatibilidad de tipos permite operar expresiones de tipos diferentes pero comnmente fuerza conversiones de tipo hacia el tipo ms restrictivo. Este efecto recibe el nombre de conversin o coercin de tipos La coercin de tipos es el proceso mediante el cual el sistema de tipos convierte la subexpresin menos restrictiva hacia la ms restrictiva cuando ambas son de distinto tipo Pascal
2 + 3.5 4.2 * 3

C
a + 3.5 (int) 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 expresin en C que convierte el carcter a a entero y de ah a flotante para poder operarse. Estas conversiones se llaman implcitas. Sin embargo la ultima fuerza a convertir el flotante a entero con perdida de informacin. Esta conversin es explcita

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Verificacin de tipos sobre operadores


La mayora de operadores de un lenguaje de programacin 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
operador + Byte Integer Word tipos Real Double Boolean Char String Constructores de tipos ... Matriz de compatibilidad para el operador + Byte Byte Word Real Error Error Error Integer Integer Word Real Error Error Error Word Word Word Word Real Error Error Error Real Real Real Real Real Error Error Error Double Boolean Char String 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 Coercin o error

Integer Integer

Double Double

Double Double Double

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Sistemas de tipos

Elementos de un sistema de tipos


La descripcin del sistema de tipos de un lenguaje de programacin se articula, fundamentalmente, mediante la definicin de los tipos primitivos y constructores compuestos, sus reglas de composicin, las operaciones permitidas sobre cada uno de ellos y el tipo de resultado que provocan

Verificacin de tipos sobre subprogramas


La sobrecarga es un trmino 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, nmero, tipo u orden de parmetros formales en su declaracin. La implementacin invocada depender de los tipos aplicados en la expresin de llamada La sobrecarga de subprogramas es la capacidad de un lenguaje de permitir la declaracin de varios procedimientos o funciones con el mismo nombre pero distinto, nmero, tipo u orden de parmetros 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 (m, n: TMatriz; VAR r:TMatriz); ... PROCEDURE sumar (v, w: TVector; VAR r:TVector); ...

El procedimiento de suma est sobrecargado. La sobrecarga de subprogramas es ms propia de lenguajes orientados a objetos que de lenguajes estructurados. En ese contexto se llama polimorfismo

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Qu es un comprobador de tipos?
Una vez descritas las responsabilidades y caractersticas de un sistema de tipos, es preciso implementarlo en la practica a travs de los mecanismos proporcionados por los esquemas de traduccin 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 travs de mecanismos de traduccin dirigida por la sintaxis

Ejemplo
expresion ::= expresion:e1 MAS expresion:e2 {: t1 = <<recuperar tipo de e1>> t2 = <<recuperar tipo de e2>> No: <<Error semntico>> si: <<otras acciones semnticas>> :}

<<Comprobar la compatibilidad de t1 y t2 para +>>

En el caso de las expresiones es necesario recuperar el tipo de cada subexpresin y comprobar su compatibilidad con respecto al operador que las combina. Si no son compatibles se emite un mensaje de error semntico

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte
ScopeManager

SymbolTable

Scope

TypeTable

SymbolIF

TypeIF

SemanticErrorManager SymbolBase SymbolBase

SymbolConstant SymbolVariable

SymbolProcedure SymbolFunction SymbolParameter TypeSimple TypeRecord TypeUnion TypeArray TypeEnum TypeSet

TypePointer TypeProcedure

TypeFunction

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Tipos primitivos y constructores de tipos


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 mtodos 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 ()

Propiedad name y scope


Todos los tipos contienen un nombre y un mbito de declaracin por tanto pueden factorizarse en la clase abstracta TypeBase e implementar sendos mtodos de consulta y modificacin

Equivalencia de tipos
Para mantener limpio el cdigo del esquema de traduccin en Cup se recomienda implementar la equivalencia de tipos nominal o estructural en este mtodo

Sobrescritura de mtodos de Object


Igualmente se recomienda la implementacin de los mtodos hashcode y toString heredados de clase Object. Consulte tema 6 para obtener detalles

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Tipos primitivos y constructores de tipos


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 mtodos necesarios para caracterizarlos (entendemos que los nombres de cada clase son autoexplicativos)
TypeSet -String name -ScopeIF scope -TypeIF base TypeArray -String name -ScopeIF scope -int min -int max -TypeIF base -String name -ScopeIF scope -int size TypeRecord -String name -Map <String, TypeIF> fields -ScopeIF scope TypeSimple -String name -Map <String, TypeIF> baseFields -Map <String, Map<String, TypeIF>> variants -ScopeIF scope TypeEnum -String name -List <String> values -ScopeIF scope TypeProcedure -String name -List <TypeIF> parameters -ScopeIF scope TypeUnion TypePointer -String name -TypeIF base -ScopeIF scope TypeFunction -TypeIF rType -ScopeIF scope

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Smbolos
Las clases que implementan la interfaz SymbolIF representan smbolos que han sido declarados por el usuario programador dentro del lenguaje. Para cada tipo de smbolo tambin 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 ()

Propiedad name y scope


Todos los smbolos contienen un nombre y un mbito de declaracin por tanto pueden factorizarse en la clase abstracta SymbolBase e implementar sendos mtodos de consulta y modificacin

Propiedad Tipo
De forma similar, todos los smbolos declarados tienen un tipo, ya sea declarado explcitamente o inferido luego el tipo es otra propiedad que puede factorizarse en esta clase

Sobrescritura de mtodos de Object


Igualmente se recomienda la implementacin de los mtodos hashcode y toString heredados de clase Object. Consulte tema 6 para obtener detalles

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Smbolos
Las clases que implementan la interfaz SymbolIF representan smbolos que han sido declarados por el usuario programador dentro del lenguaje. Para cada tipo de smbolo tambin conviene identificar los atributos propios que lo caracterizan de manera preliminar
SymbolConstant -String name -TypeIF type -Number value -ScopeIF scope SymbolVariable -String name -TypeIF type -ScopeIF scope -Integer Address SymbolFunction -String name -TypeIF Type -ScopeIF scope SymbolProcedure -String name -TypeIF type -ScopeIF scope SymbolParameter -String name -TypeIF type -ScopeIF scope

Al igual que en el caso de los tipos, estas definiciones sern potencialmente extendidas cuando avancemos en la construccin del compilador

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Gestor de errores semnticos y trazabilidad


Cuando se detecta un error semntico durante el proceso de compilacin, el comprobador de tipos debe emitir un mensaje de error. El gestor de errores proporciona mtodos para informar de errores recuperables y no recuperables as como para trazar la ejecucin del comprobador semntico
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); :}
SemanticErrorManager + void semantiDebug (String message) + void semanticInfo (String message) + void semanticWarn (String message) + void semanticError (String message) + void semanticFatal (String message)

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Tablas de tipos
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); } } ... } public boolean containsType (TypeIF type) { String id = type.getName (); return containsType (id); } public boolean containsType (String id) { return tTable.containsKey (id); TypeTable (contina) public void addType (String id, TypeIF type){ type.setScope (this.scope); tTable.put (id, type);

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Tablas de smbolos
Una tabla de smbolos 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); } } ... } public boolean containsSymbol (SymbolIF symbol) { String id = symbol.getName (); return containsSymbol (id); } public boolean containsSymbol (String id) { return sTable.containsKey (id); SymbolTable(contina) public void addSymbol (String id, SymbolIF symbol){ symbol.setScope (this.scope); sTable.put (id, symbol);

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

mbitos de declaracin
El mbito es un artefacto que representa un mbito de declaracin dentro de un programa. Estructuralmente es un contenedor que mantiene una referencia a la tabla de smbolos y la tabla de tipos asociada con dicho mbito
Scope

Nombre del mbito


En bloques con nombre, como procedimientos y funciones, contiene el nombre del bloque

-String name -int id -int level -symbolTable -typeTable

Identificador de mbito
Cada mbito dispone de un identificador nico asignado, correlativamente, por construccin

Tabla de smbolos y tipos


Cada mbito contiene una referencia a una tabla de smbolos y otra de tipos inicialmente vaca asignadas por construccin

+ String getName () + int getId () + int getLevel () + SymbolTableIF getSymbolTable () + TypeTableIF getTypeTable ()

Nivel de anidamiento del mbito


El nivel de anidamiento indica el grado de profundidad en el que se encuentra el bloque sintctico asociado al mbito comenzando por 0 para el mbito global

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Gestor de mbitos
El gestor de mbitos es un artefacto que mantiene una relacin de todos los mbitos creados durante el proceso de compilacin. Este objeto es una nica instancia en el compilador y sirve para gestionar la bsqueda de smbolos y tipos a travs de los mbitos activos
Crea un nuevo mbito, annimo o con nombre. Aplicable cuando se entra en un bloque Recupera el ltimo mbito abierto o aqul en cierto nivel de profundidad de los actualmente abiertos Busca smbolos o tipos a lo largo de los mbitos activos, comenzando por el ms reciente y avanzando en la jerarqua de mbitos padre
ScopeManager + void openScope () + void openScope (String name) + void closeScope () + ScopeIF getCurrentScope () + ScopeIF getScope (int level) + List<ScopeIF> getAllScopes () + SymbolIF searchSymbol () + TypeIF searchType () + boolean containsSymbol (String id) + boolean ContainsType (String id)

Cierra un mbito. Aplicable cuando se abandona un bloque Recupera todos los mbitos creados (abiertos o cerrados) durante el proceso de compilacin. til en la generacin de cdigo final Indica si un smbolo o tipo se encuentra declarado dentro de algn mbito activo

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Gestor de mbitos
La manera de gestionar los mbitos activos es a travs de una pila de mbitos interna. Cada operacin openScope crea un nuevo objeto Scope y lo apila sobre la pila. Cada operacin closeScope, saca el mbito de la cima de la pila

Ejemplo
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; ...

ScopeManager

ScopeManager

ScopeManager

Validar[2] Leer[1] Global [0] Global [0] Leer[1] Global [0]

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Artefactos de un comprobador de tipos

Consltese el documento directrices de implementacin

Antes de comenzar con la descripcin de la implementacin del comprobador de tipos para cada parte de la gramtica de un lenguaje es necesario presentar la coleccin de artefactos que sern utilizados dentro de la misma. A continuacin describimos aquellos que aparecen en el framework de soporte

Gestor de mbitos
La manera de gestionar los mbitos activos es a travs de una pila de mbitos interna. Cada operacin openScope crea un nuevo objeto Scope y lo apila sobre la pila. Cada operacin closeScope, saca el mbito de la cima de la pila

Ejemplo
... 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 ... ... BEGIN Leer (v); Leer (w); v := suma (v, w); END. ScopeManager

ScopeManager

ScopeManager

Leer[1] Global [0]

suma[1] Global [0] Global [0]

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Apertura del mbito inicial


program cabecera ::= cabecera cuerpo; ::= PROGRAM ID:id PYC {:

Declaracin de constantes
dConstante ::= ID:id IGUAL rNumero:rn {: ScopeIF scope = scopeManager.getCurrentScope (); SymbolTable sTable = scope.getSymbolTable (); String name = id.getLexema (); Object value = rn.getValue (); if (sTable.containsSymbol (name)) { semanticErrorManager.semanticFatalError (...); } else { TypeIF type; if (value instanceof Integer) type = TypeSimple.ENTERO; if (value instanceof Float) type = TypeSimple.REAL; if (value instanceof Boolean) type = TypeSimple.LOGICO; SymbolConstant sC = new SymbolConstant (name, value, type); sTable.add (sC); :}; rNumero ::= NUM:n {: RESULT = new RNumero (n.getValue ()); :} | ; | ;

String name = id.getLexema (); scopeManager.openScope (name); // Insertar todos los TypeSimple en la TT :}; cuerpo ::= declaraciones bSentencias; {: :} scopeManager.closeScope (); declaraciones ::= bConstantes bTipos bVariables dSubprogramas; bConstantes bTipos bVariables dConstantes ::= CONST dConstantes | ; ::= TYPE dTipos ::= VAR dVariables | dConstante;

::= dConstantes dConstante

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Declaracin de constantes
| ID:id {: { String name = id.getLexema (); if (scopeManager.containsSymbol (name)) if (s instanceof SymbolConstant) { SymbolConstant sC = (SymbolConstant) s; Object value = sC.getValue(); RESULT = new RNumero (value); } else semanticErrorManager.semanticFatalError (...); } else semanticErrorManager.semanticFatalError (...); :}; dTipos ::= dTipos dTipo | dTipo; SymbolIF s = scopeManager.searchSymbol (name);

Declaracin de tipos
dTipo ::= ID:id IGUAL tipo:t {: String name = id.getLexema (); TypeIF type = t.getType (); type.setName (name); ScopeIF scope = scopeManager.getCurrentScope (); TypeTableIF tTable = scope.getTypeTable (); tTable.addType (name, type); :}; tipo ::= dArray:t | rTipo:t rTipo ::= INTEGER | REAL {: RESULT = new Tipo (t.getType()); :} {: RESULT = new Tipo (t.getType()); :}; {: RESULT = new RTipo (TypeSimple.ENTERO); :} {: RESULT = new RTipo (TypeSimple.REAL); :} | dRecord:t {: RESULT = new Tipo (t.getType()); :}

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Declaracin de tipos
| ID:id {: String name = id.getLexema (); if (scopeManager.containsType (name)) RESULT = new Rtipo (type); } else semanticErrorManager.semanticFatalError (...); :} dArray ::= ARRAY rArray:t PYC {: RESULT = new dArray (t.getType ()); :}; rArray ::= CI rNumero::n1 DD rNumero:n2 CD rArray:t {: Object min = n1.getValue (); Object max = n2.getValue (); TypeIF tBase = t.getType (); TypeIF tArray = new TypeArray (min, max, tBase); RESULT = new RArray (tArray); :} { TypeIF type = scopeManager.searchType (name);

Declaracin de tipos
| OF rTipo:t {: RESULT = new RArray (t.getType()); :}; dRecord ::= RECORD BEGIN bCampos:t END {: RESULT = new DRecord (t.getTypeRecord ()); :}; bCampos ::= bCampos:t1 lCampos:t2 PYC {: TypeRecord tR1 = t1.getTypeRecord (); TypeRecord tR2 = t2.getTypeRecord (); TypeRecord tR = new TypeRecord (); if (tr2.constainsSome (tR1)) semanticErrorManager.semanti... else { tR.addAllFields (tR1); tR.addAllFields (tR2); RESULT = new BCampos (tR); } :} | lCampos:t {: TypeRecord tR = t.getTypeRecord (); RESULT = new BCampos (tR); :}; :};

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Declaracin de tipos
lCampos ::= ID:id DP rTipo:t {: String name = id.getLexema (); TypeIF type = t.getType (); TypeRecord tR = new TypeRecord (); tR.addField (name, type); RESULT = new LCampos (tR, type); :} | ID:id COMA lCampos:t {: String name = id.getLexema (); TypeRecord tR = t.getTypeRecord (); if (!(tR.containsField (name))) { TypeIF type = t.getType (); tR.addField (name, type); } else semanticErrorManager.semanticFatalError (...); :}

Declaracin de variables
dVariables ::= dVariables dVariable | dVariable; dVariable ::= ID:id DP rTipo:t {: String name = id.getLexema (); TypeIF type = t.getType (); ScopeIF scope = scopeManager.getCurrentScope (); SymbolTable sTable = scope.getSymbolTable (); if (!(sTable.containsSymbol (name))) { SymbolVariable sV = new SymbolVariable (name, type); sTable.addSymbol (name, sV); RESULT = new DVariables (type); } else semanticErrorManager.semanticFatalError (...); :} | ID:id COMA dVariables:t {: <<igual que en la regla anterior>> :};

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Declaracin de subprogramas
dSubprogramas ::= dSubprogramas dSubprograma dSubprograma ::= dProcedimiento | dFuncion; dProcedimiento ::= pCabecera cuerpo; dFuncion ::= fCabecera cuerpo; fCabecera ::= FUNCTION ID:id PI pFormales:pfs PD DP rTipo:t {: String name = id.getLexema (); if ((!scopeManager.containsSymbol (name)) && (scopeManager.containsType (name))) { ScopeIF pScope = scopeManager.getCurrentScope (); SymbolTableIF psTable = pScope.getSymbolTable (); TypeTableIF ptTable = pScope.getTypeTable (); TypeFunction tF = new TypeFunction (name); SymbolFunction sF = new SymbolFunction (name, tF); ScopeIF scope = scopeManager.openScope (name); SymbolTableIF sTable = scope.getSymbolTable (); for (SymbolParameter p : pfs.getParameters()) { sTable.addSymbol (p.getName (), p); | ;

Declaracin de subprogramas
tF.addParameterType (p.getType ()); } tF.setReturnType (t.getType ()); ptTable.addType (name, tF); psTable.addSymbol (name, sF); } else semanticErrorManager.semanticFatalError (...); pFormales ::= lPFormales:lpf {: List <SymbolParameter> lp = lpf.getParameters (); RESULT = new PFormales (lp); :} | {: RESULT = new pFormales (); :} ; lPFormales ::= lPFormales:lpf PYC dPFormales:dpf {: List <SymbolParameter> lp = dpf.getParameters (); lpf.addAllParameters (lp); RESULT = lpf; :} | dPFormales:dpf {: List <SymbolParameter> lp = dpf.getParameters (); RESULT = new LPFormales (lp); :}; :}

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Declaracin de subprogramas
dPFormales ::= ID:id PD rTipo:t PYC {: String n = id.getLexema (); TypeIF type = t.getType (); SymbolParameter sp = new SymbolParameter (n,type); RESULT = new DPFormales (sp, type); :} | ID:id COMA dPFormales:dpf {: String n = id.getLexema (); TypeIF type = dpf.getType (); SymbolParameter sp = new SymbolParameter(n,type); dpf.addParameter (sp, type); RESULT = dpf; :}; pCabecera ::= FUNCTION ID:id PI pFormales:pfs PD {: << igual que en fCabecera pero sin tipo de retorno e insertandoun Symbol/Type Procedure en vez de un Symbol/Type Function >> :};

Expresiones
exp ::= exp:e1 MAS exp:e2 {: TypeIF t1 = e1.getType (); TypeIF t2 = e2.getType (); if (t1.isCompatible (t2, TypeSimple.MAS)) { TypeIF t = t1.cast (t2, TypeSimple.MAS); RESULT = :} | exp MENOS exp {:...:} | exp POR exp | exp DIV exp | rNumero:rn {: Object value = rn.getValue (); TypeIF type; if (value instanceof Integer) type = TypeSimple.ENTERO; if (value instanceof Float) RESULT = new Exp (type); :} type = TypeSimple.REAL; {:...:} {:...:} new Exp (t); } else semanticErrorManager.semanticFatalError (...);

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Expresiones
| PI exp:e PD {: TypeIF type = e.getType (); RESULT = new Exp (type); :} | fCall:fc {: TypeIF type = fc.getType (); RESULT = new Exp (type); :} | referencia:ref {: TypeIF type = ref.getType (); RESULT = new Exp (type); :} referencia ::= ID:id {: String name = id.getLexema (); if (scopeManager.contains (name)) { SymbolIF sId = scopeManager.searchSymbol (name);

Expresiones
TypeIF type = sId.getType (); RESULT = new :} | referencia:r PTO ID:id {: String name = id.getLexema (); TypeIF rType = r.getType (); if (rType instanceof TypeRecord) { TypeRecord tRec = (TypeRecord) rType; if (tRec.containsField (name)) { Type innerType = tRec.getType (name); RESULT = new Referencia (innerType); } else SemanticErrorManager.semanticFactalError (...); } else SemanticErrorManager.semanticFactalError (...); :} Referencia (type); } else SemanticErrorManager.semanticFactalError (...);

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Expresiones
| referencia:r CI exp:e CD {: TypeIF eType = e.getType (); if (eType.equals(TypeIF.ENTERO)) { Type rType = r.getType (); if (rType instanceof TypeArray) { TypeArray arrayType = (TypeArray) rType; Type innerType = arrayType.getBaseType (); RESULT = new Reference (innerType); } else SemanticErrorManager.semanticFatal...; } else SemanticErrorManager.semanticFatal...; :} fCall ::= ID:id PI pActuales:pa PD {: String name = id.getLexema (); if (scopeManager.containsSymbol (name)) { SymbolIF s = scopeManager.searchSymbol (name); if (s instanceof SymbolFunction) { SymbolFunction sf = (SymbolFunction)s;

Expresiones
TypeFunction tF = sF.getType (); List <TypeIF> aParams = pa.getParameterTypes (); List <TypeIF> fParams = tF.getParameters (); if (aParams.equals(fParams)) { TypeIF returnType = tF.getReturnType (); RESULT = new Fcall (returnType); } else SemanticErrorManager.semanticFatalError (...); } else SemanticErrorManager.semanticFatalError (...); } else SemanticErrorManager.semanticFatalError (...); :}; pActuales ::= pActuales:pa COMA exp:e {: TypeIF type = e.getType(); pa.addParameterType (type); RESULT = pa; :} | exp:e {: TypeIF type = e.getType (); RESULT = new PActuales (type); :};

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Implementacin de un comprobador de tipos sencillo en cup


Una vez conocidos los artefactos necesarios para implementar un comprobador de tipos, podemos describir la forma del esquema de traduccin para cada seccin de una gramtica. En nuestros ejemplos utilizaremos una estilo Pascal aunque es fcilmente trasladable a C. Como cup no admite atributos heredados la solucin propuesta se ver someramente condicionada por este hecho

Sentencias
cuerpo ::= BEGIN lSentencias END PYC; lSentencias ::= lSentencias sentencia | ; sentencia ::= sentenciaIf | sentenciaWhile | sentenciaAsignacion | pCall | cuerpo; sentenciaIf ::= IF PI exp:e PD THEN sentencia ELSE sentencia {: TypeIF type = e.getType (); if (!(type instanceof TypeSimple.LOGICO)) semanticErrorManager.semanticFatalError (...); :} SentenciaWhile ::= WHILE PI exp:e PD DO sentencia {: <<igual que sentenciaIf>> :}

Sentencias
sentenciaAsignacin ::= referencia:r TypeIF eType = e.getType (); TypeIF rType = r.getType (); if (rType instanceof TypeFunction || rType instanceof TypeProcedure) { rType = rType.getReturnType (); ScopeIF currentScope = scopeManager.getCurrentScope(); if (!r.getScope().equals(currentScope)) semanticErrorManager.semanticFatalError (...); } if (!(rType.isCompatible (eType, TypeIF.IGUAL))) semanticErrorManager.semanticFatalError (...); :} pCall ::= ID:id PI pActuales:pa PD PYC; {: <<igual que fCall>> :} IGUAL exp:e PYC {:

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Temas avanzados de un comprobador de tipos


Una vez esbozado el esquema de traduccin en cup para un comprobador de tipos sencillo sobre una gramtica estilo Pascal, estamos en disposicin de tratar algunos temas avanzados de inters que discutimos a continuacin

Inferencia de tipos
La inferencia de tipos es el proceso por el cual un comprobador de tipos infiere el tipo de una construccin gramatical a partir de su contexto. Esto es especialmente til en lenguajes sin tipificacin explcita Ejemplo I
dConstante ::= ID:id IGUAL numero:n {: ... String name = id.getLexema (); Object value = n.getValue (); TypeIF type; if (value instanceof Integer) type = TypeSimple.ENTERO; if (value instanceof Float) type = TypeSimple.REAL; if (value instanceof Boolean) type = TypeSimple.LOGICO; SymbolConstant sC = new SymbolConstant (name, value, type); sTable.add (sC); :};

dConstante ID = numero
(3.5)

El caso ms sencillo de inferencia de tipos se produce en la declaracin de constantes. En efecto, el tipo de la constante no se declara explcitamente sino que se infiere del valor asignado a la misma en la declaracin

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Temas avanzados de un comprobador de tipos


Una vez esbozado el esquema de traduccin en cup para un comprobador de tipos sencillo sobre una gramtica estilo Pascal, estamos en disposicin de tratar algunos temas avanzados de inters que discutimos a continuacin

Inferencia de tipos
La inferencia de tipos es el proceso por el cual un comprobador de tipos infiere el tipo de una construccin gramatical a partir de su contexto. Esto es especialmente til en lenguajes sin tipificacin explcita Ejemplo II
exp ::= ID:id {: String name = id.getLexema (); if (!scopeManager.containsSymbol (name)) { ScopeIF scope = scopeManager.getCurrentScope (); SymbolTable sTable = scope.getSymbolTable (); SymbolVariable sV = new SymbolVariable (); sv.setTypes (TypeSimple.ALL); sTable.addSymbol (name, sV); } RESULT = new Exp (sV, TypeSimple.ALL); :}

exp exp numero


(3.5) {Real}

exp ID

{Byte, Word, Integer, Real, Double}

En el caso de lenguajes construccin sintctica nicos posibles valores numricos, descartando String

sin tipificacin, la infiere que los para ID son los Boolean, Char y

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Temas avanzados de un comprobador de tipos


Una vez esbozado el esquema de traduccin en cup para un comprobador de tipos sencillo sobre una gramtica estilo Pascal, estamos en disposicin de tratar algunos temas avanzados de inters que discutimos a continuacin

Inferencia de tipos
La inferencia de tipos es el proceso por el cual un comprobador de tipos infiere el tipo de una construccin gramatical a partir de su contexto. Esto es especialmente til en lenguajes sin tipificacin explcita Ejemplo II
exp ::= exp:e1 MAS exp:e2 {: TypeIF t1 = e1.getTypes (); TypeIF t2 = e2.getTypes (); t1.removeIncompatibles (t2, TypeIF.MAS); t2.removeIncompatibles (t1, typeIF.MAS); SymbolVariable e1Var = e1.getVariable(); SymbolVariable e2Var = e2.getVariable(); if (e1Var != null) e1Var.setTypes (t1); if (e2Var != null) e1Var.setTypes (t2); if (t1.isEmpty() || t2.isEmpty) semanticErrorManager...; else { Types types = new Types (t1); types.addAllTypes (t2); RESULT = new Exp (types); :} }

exp exp numero


(3.5) {Real}

exp ID

{Byte, Word, Integer, Real, Double}

En el caso de lenguajes construccin sintctica nicos posibles valores numricos, descartando String

sin tipificacin, la infiere que los para ID son los Boolean, Char y

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Temas avanzados de un comprobador de tipos


Una vez esbozado el esquema de traduccin en cup para un comprobador de tipos sencillo sobre una gramtica estilo Pascal, estamos en disposicin de tratar algunos temas avanzados de inters que discutimos a continuacin

Sobrecarga de subprogramas
Como ya se coment anteriormente, existen lenguajes que dan soporte a la sobrecarga de subprogramas. Es decir permiten declarar varias versiones de funciones o procedimiento con igual nombre pero distinto numero, tipo u orden de parmetros. La implementacin de esta capacidad se estudiar para funciones ya que para procedimientos resulta similar En la declaracin
fCabecera ::= FUNCTION ID:id PI pFormales:pfs PD DP rTipo:t {: String name = id.getLexema (); TypeIF t = <<tipo para esta versin de la funcin>> TypeFunction tFun = <<buscar el tipo por name>> tFun.addSignature (t); ... :}

En la invocacin
fCall ::= ID:id PI pActuales:pa PD {: String name = id.getLexema (); if (scopeManager.containsSymbol (name)) { TypeIF t = scopeManager.searchType (name); if (t instance of TypeFunction) { TypeFunction tF = (TypeFunction) t; List<List<TypeIF>> signatures = tF.getSignatures (); for (<<cada signatura>>) { <<proceder como antes comprobando que alguna signatura encaja con la llamada actual>> } } }

Ahora cada TypeFunction contiene una coleccin de signaturas (una lista interna de lo que antes era TypeFunction). Al declarar una nueva funcin ha de aadirse a la lista

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Temas avanzados de un comprobador de tipos


Una vez esbozado el esquema de traduccin en cup para un comprobador de tipos sencillo sobre una gramtica estilo Pascal, estamos en disposicin de tratar algunos temas avanzados de inters que discutimos a continuacin

Recuperacin de errores semnticos


La recuperacin de errores semnticos es algo menos frecuente que la de errores sintcticos. Sin embargo es posible realizarlo mediante una extensin conveniente del esquema de traduccin dirigido por la sintaxis. En concreto se utilizan para expresiones y sentencias dos nuevos valores para el atributo de tipo: error y correcto. sentencia .tipo = error IF
Boolean = tipo. exp

exp .tipo = error >

THEN

sentencia .tipo = Correcto

exp .tipo = ENTERO 4

Los errores se almacenan como formas de tipo y se van acumulando para indicar qu partes del rbol de anlisis sintctico contiene error

Javier Vlez Reyes jvelez@lsi.uned.es

true

Anlisis semntico. Comprobacin de tipos


Construccin de comprobadores de tipos en la prctica

Desarrollo paso a paso


La fase de anlisis semntico requiere de la implementacin del comprador de tipos del lenguaje que siga las directrices del sistema de tipos del lenguaje. En esta ltima seccin hemos descrito cmo se implementa un comprobador de tipos sencillos. Ahora damos la secuencia de pasos a realizar 1. Implementacin de artefactos en cdigo abierto 1. Implementacin de la tabla de smbolos 2. Implementacin de la tabla de tipos 3. Implementacin de todas las clases TypeIF necesarias 4. Implementacin de todas las clases SymbolIF necesarias 5. Incorporacin de atributos y constructores necesarios en cada no terminal 2. Implementacin de las acciones semnticas del comprobador de tipos 1. Acciones para declaraciones 2. Acciones para expresiones 3. Acciones para sentencia 3. Prueba del comprobador de tipos 1. Ejecutar finalTestCase 2. Comprobar en la traza el estado de cada tabla de smbolos y tipo 3. Comprobar que se emiten los mensajes de error semntica ante cdigos incorrectos Javier Vlez Reyes jvelez@lsi.uned.es
Se debe reflexionar cuidadosamente acerca de qu atributos son necesarios para caracterizar cada tipo y smbolo del lenguaje y cada elemento no gramatical

Anlisis semntico. Comprobacin de tipos


Bibliografa

Material de estudio
Bibliografa bsica
Construccin de compiladores: principios y prctica Kenneth C. Louden International Thomson Editores, 2004 ISBN 970-686-299-4

Javier Vlez Reyes jvelez@lsi.uned.es

Anlisis semntico. Comprobacin de tipos


Bibliografa

Material de estudio
Bibliografa complementaria
Compiladores: Principios, tcnicas y herramientas. Segunda Edicin Aho, Lam, Sethi, Ullman Addison Wesley, Pearson Educacin, Mxico 2008

Diseo de compiladores. A. Garrido, J. Iesta, F. Moreno y J. Prez. 2002. Edita Universidad de Alicante

Javier Vlez Reyes jvelez@lsi.uned.es

S-ar putea să vă placă și