Documente Academic
Documente Profesional
Documente Cultură
Anlisis semntico II
Comprobacin de tipos
Javier Vlez Reyes jvelez@lsi.uned.es Departamento de Lenguajes Y Sistemas Informticos UNED
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
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
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
>
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
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
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.
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
mbito nivel 1
mbito nivel 2
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
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
Tema 7
E > E
Anlisis semntico
WHILE
S E DO S
Tema 8-9
E > E
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
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
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
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
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)
SET (ENTERO)
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)
(Tvector) TVector
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;
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 Equivalente Equivalente
T2
T3
No equivalente Equivalente
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
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
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. 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
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
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
Integer Integer
Double Double
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
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>> :}
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
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
SymbolConstant SymbolVariable
TypePointer TypeProcedure
TypeFunction
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
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
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
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 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
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
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
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);
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);
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
Identificador de mbito
Cada mbito dispone de un identificador nico asignado, correlativamente, por construccin
+ String getName () + int getId () + int getLevel () + SymbolTableIF getSymbolTable () + TypeTableIF getTypeTable ()
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
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
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
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;
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()); :}
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); :}; :};
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>> :};
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); :}; :}
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 (...);
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 (...);
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); :};
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 {:
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
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 ID
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
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 ID
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
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
THEN
Los errores se almacenan como formas de tipo y se van acumulando para indicar qu partes del rbol de anlisis sintctico contiene error
true
Material de estudio
Bibliografa bsica
Construccin de compiladores: principios y prctica Kenneth C. Louden International Thomson Editores, 2004 ISBN 970-686-299-4
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