Escolar Documentos
Profissional Documentos
Cultura Documentos
Borrador
ANALISIS SEMANTICO
EN PROCESADORES
DE LENGUAJE
(Borrador)
Departamento de Informática
Universidad de Oviedo
Noviembre-2000 (Borrador)
Análisis semántico en procesadores de lenguaje
1 INTRODUCCION
Se entiende por semántica como el conjunto de reglas que especifican el significado de cualquier sentencia
En los apartados siguientes se estudiarán las distintas formas de especificar sintácticamente los lenguajes de progra-
y especificación formal.
La especificación natural de la semántica de un lenguaje de programación, se basa en utilizar el lenguaje natural para
características semánticas, que no sean deducibles de la gramática BNF que describe sintácticamente el lenguaje.
El analizador semántico deberá comprobar las especificaciones semánticas relatadas en lenguaje natural y las especi-
analizador semántico. Normalmente, lo que se hace es incorporar rutinas (procedimientos o funciones) denominadas
semánticas, que comprueban las especificaciones descritas en lenguaje natural. Dependiendo de la complejidad de la especi-
ficación, unas veces estas rutinas semánticas serán verdaderos procedimientos o funciones, y en otros casos serán simplemente
metamatemática, y no en la definición por medio del lenguaje natural. Es, por tanto, una especificación mucho más precisa, sin
embargo requiere el uso de unas herramientas que pueden dividirse en dos grandes grupos:
-1-
Análisis semántico en procesadores de lenguaje
caciones semánticas de los lenguajes de programación. Una gramática atribuida se caracteriza por:
1.- La estructura sintáctica del lenguaje se define mediante una gramática libre de contexto.
2.- Cada símbolo de la gramática, tiene asociado un conjunto finito de atributos. Ejemplo:
<SIMBOLO>.a.b.c
siendo a, b y c atributos.
Cada atributo puede tomar un conjunto de valores (posiblemente infinito). El valor de un atributo describe una propiedad
3.- Cada regla de producción debe de especificar como se modifican los atributos con su aplicación.
4.- Una gramática atribuida describe un sublenguaje (del lenguaje definido) mediante las condiciones de contexto que
deben ser cumplidas por los valores de los atributos. Una sentencia sintácticamente correcta también lo será
semánticamente si y sólo si todos los atributos satisfacen las condiciones del contexto.
El principal problema del proceso de gramáticas atribuidas, es que el tiempo necesario para realizar las comprobaciones
[FRAS94].
Los lenguajes de especificación semántica se pueden dividir en dos grandes grupos orientados a definir un modelo y
Las ventajas de usar lenguajes orientados a definir un modelo, estriban en que la existencia de un modelo asegura la
consistencia de la especificación, y que el modelo puede generar sugerencias al implementador. Por otra parte, la existencia
de un modelo incrementa la probabilidad de que un sistema sea implementable en un sistema físico concreto.
Las desventajas son por un lado que el uso de un modelo restringe y limita al implementador y, por otro, que puede
admitir más información de la estrictamente necesaria para describir las propiedades de la abstracción que se está considerando.
El primero de los lenguajes orientados a definir un modelo es el VDM (Vienna Develpment Method) usado para describir
Posteriormente se desarrolló el HDM ( " Hierarchical Development Method ") basado en la especificación de programas
-2-
Análisis semántico en procesadores de lenguaje
operación, no definiendo, por tanto, ni tipos de datos ni valores. El ejemplo más conocido es el método algebraico de espe-
análisis, diseño y desarrollo de software. En estos casos, el lenguaje de especificaciones semánticas, también se utiliza como
lenguaje de programación para el desarrollo de software. Ejemplos: VDL, VDM, ANNA, GYPSY, HDM, ...
de un lenguaje de programación fue desarrollada en los laboratorios de Viena de IBM con la especificación formal del lenguaje
PL/I (Lucas y Walk 1969). Se utilizó la notación denominada VDL (Vienna Definition Language).
la semántica denotacional. Es una metodología de desarrollo de software con fuerte base matemática y formal.
VDM no está relacionado con VDL, pese a la conexión de Viena. Se caracteriza por:
- Construye métodos formales abstractos de las especificaciones.
- Emplea una notación formal matemática basada en los principios de la semántica denotacional.
- Cualquier procedimiento se escribe utilizando tipos abstractos de datos, y con rigurosas especificaciones.
- entradas
- salidas
-3-
Análisis semántico en procesadores de lenguaje
3. Funciones semánticas, son las abstracciones de los cambios de estado y de las posibles salidas.
Se ha utilizado en desarrollo de bases de datos, definición de la semántica de lenguajes y para el desarrollo de compi-
- Consta de:
b) Un conjunto de lenguajes.
- La mayor parte de la notación de HDM es el lenguaje de especificaciones denominado SPECIAL, el cual se utiliza
- SPECIAL es una notación formal que permite definir los tipos de los argumentos y de los valores devueltos por un
- El lenguaje SPECIAL permite la ocultación de la información (information hiding) entre distintos niveles.
- Las facilidades disponibles en cada nivel están implementadas utilizando las facilidades del nivel inmediato inferior.
- El nivel más bajo o inferior es el que soporta las primitivas que se encuentran implementadas en el propio sistema
físico.
fases de desarrollo.
- anotaciones
-4-
Análisis semántico en procesadores de lenguaje
- anotaciones de sentencia
- anotaciones de excepción
- anotaciones de visibilidad
- ANNA incluye un pequeño número de atributos predefinidos, los cuales sólo pueden aparecer en las anotaciones.
- La estructura léxica de ANNA está diseñada de forma que las extensiones de Ada aparezcan como comentarios siendo
- La semántica de las anotaciones está definida en términos de conceptos de Ada. De hecho, muchas anotaciones son
generalizadas de conceptos restringidos. Ello simplifica y facilita a los programadores de Ada la especificación
- El sistema ANNA intenta proporcionar una estructura en la cual diferentes teorías de especificación formal puedan
- El sistema ANNA, como lenguaje de especificación formal, se puede aplicar a otros lenguajes diferentes del Ada
especificaciones y código.
- Sistema de comprobación en tiempo de ejecución el cual traduce las especificaciones formales en rutinas
de chequeo Ada.
2.2.2.3.5 GYPSY
Se comenzó a desarrollar en 1974 en la Universidad de Texas.
Incluye:
- metodología
- lenguaje
- El GYPSY se puede usar tanto como lenguaje de especificación como lenguaje de programación.
-5-
Análisis semántico en procesadores de lenguaje
terminales) se les asocia un conjunto de atributos. Además a cada regla sintáctica se le asocian unas reglas semánticas que
3.1 Atributos
Un atributo es una variable que representa una determinada propiedad del símbolo X y que puede tomar un valor
cualquiera dentro de un conjunto de valores posibles. Los atributos se denotan con un nombre precedido por un punto y el
nombre del símbolo al que está asociado. Por ejemplo X.tipo, X.valor, etc... Al conjunto de atributos asociados al símbolo X,
El atributo habitual en los compiladores es el tipo, sin embargo en los intérpretes puros se pueden tener dos atributos:
el tipo y el valor.
(5) <OPERADOR> → +
(6) <OPERADOR> → -
Ejemplos de sentencias de este lenguaje son: c = 1+1, velocidad = 3.2 + 4 - 5.0, tocino = 4 - 5.1.
-6-
Análisis semántico en procesadores de lenguaje
El símbolo terminal identificador no tiene el atributo tipo en este ejemplo dado que no existe declaración obligatoria
de variables en esta gramática. En el caso de una gramática con declaraciones obligatorias, en la zona de declaraciones es donde
a cada una de las reglas sintácticas. Al conjunto de reglas semánticas asociado al conjunto de reglas sintácticas P se denominará
R(P). Las reglas semánticas se definen en función de los atributos de los demás símbolos que componen la regla y otras posibles
acciones. Dado que los símbolos de una gramática pueden definirse recursivamente y repetidamente en las reglas sintácticas
de la gramática, las reglas semánticas los distinguen por medio del uso de subíndices numerados de izquierda a derecha de las
reglas. En las reglas también pueden incluirse otras acciones como cambiar de tipo, imprimir, etc... que se denominarán acciones
semánticas.
Además de las reglas semánticas existe un conjunto de condiciones semánticas que se ejecutan sobre los atributos y
Cada símbolo de la gramática corresponde a un nodo del árbol sintáctico de una sentencia del lenguaje generado por
la gramática. Los símbolos terminales se corresponden con las hojas del árbol. En una gramática con atributos los nodos y las
hojas del árbol también contienen los atributos, que se evaluarán teniendo en cuenta las reglas semánticas.
{<VARIABLE>.valor := <EXPRESION>.valor
<VARIABLE>.tipo := <EXPRESION>.tipo}
<EXPRESION>1.tipo := <OPERADOR>.tipo
{<EXPRESION>2.tipo:=’F’;
<EXPRESION>2.valor:=FLOAT(<EXPRESION>2.valor);}
-7-
Análisis semántico en procesadores de lenguaje
<EXPRESION>3.valor:=FLOAT(<EXPRESION>3.valor);}
switch (<OPERADOR>.tipo)
{
’I’: <EXPRESION>1.valor :=op_entera(<OPERADOR>.clase, <EXPRESION>2.valor, <EX-
PRESION>3.valor); break;
{ <EXPRESION>.valor := número.valor
<EXPRESION>.tipo := número.tipo
{
if (tipo1==tipo2) return tipo1;
{
switch (op)
switch (op)
-8-
Análisis semántico en procesadores de lenguaje
ASIGNACION
VARIABLE = EXPRESION 1
valor tipo valor tipo
velocidad
número + número
80.5 20
A partir de esta información los atributos se propagan según las reglas y acciones semánticas definidas en el apartado
3.2.1, obteniéndose:
<EXPRESION>2.valor:=80.5
<EXPRESION>2.tipo:=’F’
<EXPRESION>3.valor:=20
<EXPRESION>3.tipo:=’I’
<OPERADOR>.clase:=’+’
<OPERADOR>.tipo:=’F’
<EXPRESION>1.tipo:=’F’
-9-
Análisis semántico en procesadores de lenguaje
<EXPRESION>3.tipo:=’F’
<EXPRESION>3.valor:=20.0
<EXPRESION>1.valor:=100.5
<VARIABLE>.tipo:=’F’
<VARIABLE>.valor:=100.5
<VARIABLE>.valor
<VARIABLE>.tipo
<OPERADOR>.tipo
sintetizados se denominará AS(x). Los símbolos terminales tan sólo tienen atributos sintetizados, y es el analizador léxico quien
se encarga de proporcionar los atributos. Además se supone que el símbolo inicial no tiene ningún atributo heredado.
<OPERADOR>.clase
<EXPRESION>.valor
<EXPRESION>.tipo
número.tipo
número.valor
- 10 -
Análisis semántico en procesadores de lenguaje
R=U R(P) es un conjunto finito de reglas semánticas asociadas a cada una de las reglas P de la
gramática libre de contexto G. Si entre las reglas semánticas hay acciones la gramática
X0 → X1X2X3...Xn
a:=f(a1,a2,a3,...,an)
donde f es una función y a,ai son atributos de los símbolos de la regla que componen
p.
B=U B(P) es un conjunto de condiciones asociado a cada una de las reglas P de la gramática libre
de contexto G.
X0 → X1X2X3...Xn
g(a1,a2,a3,...,an)
donde g es una función booleana y a,ai son atributos de los símbolos de la regla que
componen p.
Sea la regla p de la forma: X0 → X1X2X3...Xn se define AC(p) como el conjunto de atributos calculados mediante la
regla p:
sintáctica de la forma X → α y una regla semántica que calcula a con los atributos de los símbolos de la parte derecha de dicha
regla. Al conjunto de atributos sintetizados se denominará AS(x).
sintáctica de la forma Y → αXβ y una regla semántica que calcula a con los atributos del resto de los símbolos que forman la
- 11 -
Análisis semántico en procesadores de lenguaje
• AS(X) ∩ AH(X) = ∅
Una sentencia del lenguaje se dice que tiene todos sus atributos correctos si además cumplen todas las condiciones B(p)
de forma que se almacenan juntos los símbolos y los atributos. Dado que tan sólo se utilizan los símbolos de la parte derecha
de cada producción con lo que se pueden realizare los calculos simultáneamente a las reducciones que se efectuan en la pila.
• los atributos (sintetizados o heredados) de los símbolos de la parte derecha de la producción situados a su
derecha (X1X2X3...Xj-1)
Puede observarse que la definición impone restricciones a los atributos heredados de Xj, pero no hay restricciones para
los atributos sintetizados. Por tanto, toda gramática S-atribuida es también L-atribuida.
no terminal tendrán como argumentos los atributos heredados correspondientes al símbolo no terminal, y devolveran los atributos
- 12 -
Análisis semántico en procesadores de lenguaje
entre los símbolos de la parte derecha de la producción, indicando así el momento que deben ser ejecutadas. Es decir son de la
forma: A → {R0} X1 {R1} X2 {R2} X3...Xn {Rn} donde Ri representa una o varias acciones semánticas.
siguientes)
• Un atributo heredado asociado a un símbolo en la parte derecha de una regla, debe ser evaluado antes de que
• Una regla semántica asociada a una producción sintáctica no debe hacer referencia a los atributos sintetizados
• Un atributo sintetizado asociado a un símbolo terminal situado en la parte izquierda de una regla debe ser
evaluado después de que hayan sido evaluados todos los atributos de los que el dependa.
Con estas tres condiciones se garantiza que es posible realizar una correcta evaluación del esquema de traducción, y
También puede demostrarse que a partir de toda gramática L-atribuida es posible obtener un esquema de traducción
bien definido.
Un esquema de traducción bien definido asociado a una gramática S-atribuida tiene solamente reglas de la forma:
- Construcción de un sistema capaz de procesar definiciones formales de lenguajes por medio de gramáticas atribuidas,
El lenguaje de entrada al GAG, se llama ALADIN (A Language for Attributed DefINition) y está basado en principios
de gramáticas atribuidas. La salida del generador es el código del compilador escrito en Pascal.
Gramática
Atribuida
del GAG compilador (en Pascal)
lenguaje
(en ALADIN)
Figura 2.
- 13 -
Análisis semántico en procesadores de lenguaje
El sistema GAG ha conseguido resultados satisfactorios incluso con lenguajes de alta complejidad como el ADA, con
La evaluación de los atributos, se realiza por medio de un autómata de pila, y se hace partiendo del árbol sintáctico
construido tras el análisis del texto fuente. Cada nodo del árbol representa a un descendiente del símbolo inicial tras aplicarle
las distintas reglas de producción. Las reglas semánticas se evalúan al pasar por cada nodo. Una secuencia del análisis semántico
sería:
La siguiente fase es la traducción a Pascal de las reglas introducidas en ALADIN. Muchas de las expresiones en
ALADIN deben de descomponerse en varias sentencias de Pascal, utilizándose a veces variables temporales para los resultados
intermedios. Algunos operadores de ALADIN deben de ser traducidos como llamadas a procedimientos y funciones del lenguaje
Pascal.
- Legibilidad. Debe de formatearse y usarse identificadores lo más parecidos posibles a los usados con ALADIN.
- mensajes de error;
- traza;
c) TOLERANCIA A FALLOS. Contiene una verificación de todas las conexiones entre módulos y un sistema de
- 14 -
Análisis semántico en procesadores de lenguaje
4 COMPATIBILIDAD DE TIPOS
4.1 Introducción
Un compilador debe verificar que el programa fuente sigue las convenciones sintácticas y semánticas del lenguaje.
Esta verificación, denominada COMPROBACION ESTATICA (para distinguirla de la comprobación dinámica que se rea-
lizaría durante la ejecución del programa objeto), asegura que ciertos tipos de errores pueden ser detectados e indicados al
a) COMPATIBILIDAD DE TIPOS.
Un compilador debe de indicar un error si un operador se aplica a un operando incompatible, por ejemplo, si se desea
Las sentencias que producen la variación del flujo de un programa deben verificar hacia donde se transfiere el control
de flujo. Por ejemplo, la sentencia break del lenguaje C hace que el control de flujo abandone el bucle while más interior o una
c) COMPROBACION DE LA UNICIDAD.
Existen situaciones en las cuales un objeto sólo se define una vez. Por ejemplo, en Pascal un identificador debe de
declararse una sola vez, las etiquetas de la sentencia case deben de ser distintas, y los elementos de un tipo enumerado no deben
de repetirse.
Algunas veces, el mismo identificador debe aparecer dos o más veces. Por ejemplo, en Ada o en Modula-2, un bloque
o procedimiento tiene un identificador que aparece al comienzo y al final de la construcción. El compilador debe verificar que
En este apartado se estudiará la comprobación de tipos en profundidad. La mayoría de los compiladores de Pascal
- análisis sintáctico;
- comprobación de tipos;
Sin embargo en lenguajes más complejos, como por ejemplo el Ada, es conveniente realizar la comprobación de tipos
árbol árbol
GENERADOR
cadena sintáctico COMPROBACION sintáctico
ANALISIS DE código
de DE
SINTACTICO CODIGO intermedio
tokens TIPOS abstracto
INTERMEDIO
Figura 2
- 15 -
Análisis semántico en procesadores de lenguaje
La comprobación de tipos verifica que los tipos utilizados en un determinado contexto son los apropiados. Por ejemplo,
una construcción con el operador MOD del lenguaje Pascal exige que ambos operandos sean de tipo integer. De igual forma
que la comprobación de tipos debe verificar que se utilizan sólo índices con variables del tipo array, o que una función definida
por el ususario mantiene la correspondencia entre el número y tipo de parámetros declarados con los utilizados en la llamada.
En este apartado se mostrará la construcción de un pequeño modelo de comprobación de tipos. También se planteará
La información recogida por la comprobación de tipos, puede necesitarse para la generación de código. Por ejemplo,
el operador aritmético +, habitualmente, se utiliza para representar la adición entre enteros y reales, pero también se utiliza en
ocasiones sobre otros tipos de datos como string (concatenación), conjuntos (unión),... En este caso es necesario examinar el
contexto para determinar el significado del operador y consecuentemente generar el código correspondiente. Un símbolo que
puede representar diferentes operaciones en diferentes contextos se dice que está sobrecargado (overloaded).
La sobrecarga puede acompañarse de tipos inducidos, cuando un compilador obliga a un operando a convertirse al tipo
Otro concepto deiferente a la sobrecarga de operadores es el concepto de polimorfismo. El cuerpo de una función
aspectos:
- la noción de tipos;
"Si los dos operandos de los operadores aritméticos adición, sustracción y multiplicación son del tipo entero, entonces
"El resultado del operador unario & es un puntero al objeto referenciado por el operando. Si el tipo del operando es
Como conclusión a los ejemplos anteriores es que cada expresión tiene un tipo asociado con ella. Además los tipos
pueden construirse, por ejemplo el tipo puntero a ... es un tipo construido a partir de ... y referido a él.
- 16 -
Análisis semántico en procesadores de lenguaje
En los lenguajes C y Pascal, los tipos pueden ser básicos o construidos. Los tipos básicos son aquellos cuya estructura
interna no puede ser modificada por el programador. En Pascal los tipos básicos son: boolean, carácter, entero y real. También
se tratan como tipos básicos los tipos subrango, y los tipos enumerados.
El lenguaje Pascal permite al programador construir tipos a partir de los tipos básicos o de otros tipos construidos con
arrays, registros y conjuntos. Además, los punteros y las funciones pueden tratarse también como tipos construidos.
El tipo de una construcción de un lenguaje puede denominarse type expresion (expresión de tipo). Intuitivamente, una
expresión de tipo es cualquier tipo básico o formado por aplicación de un operador, denominado constructor de tipos, a otra
expresión de tipo. El conjunto de tipos básicos y tipos construidos depende del lenguaje a comprobar semánticamente.
1. Un tipo básico es un tipo de expresión. Entre los tipos básicos se encuentran boolean, char, integer y real. Un tipo
básico especial, es el tipo_error, que indicará un error durante la comprobación de tipos. Finalmente el tipo básico void que
2. También se pueden nombrar las expresiones de tipo, un nombre de tipo es una expresión de tipo.
3. Un constructor de tipos aplicado a expresión de tipo, es una expresión de tipo. Los constructores de tipos son:
a) arrays: si T es una expresión de tipo, entonces array(I,T) es una expresión de tipo que indica el tipo de un array con
elementos de tipo T y subíndices I. I es a menudo un rango de enteros. Por ejemplo, en Pascal, la declaración:
b)productos: Si T1 y T2 son expresiones de tipos, entonces su producto cartesiano T1xT2 es una expresión de tipos. Se
supone que x es asociativo por la izquierda.
c)registros: el tipo de un registro es, en sentido estricto, el producto de los tipos de sus campos. La diferencia entre un
registro y un producto es que los campos del registro tienen nombres. Por ejemplo, una tabla de símbolos puede construirse
según la estructura:
La comprobación de tipos en el caso de registros puede realizarse usando una expresión de tipos formada al aplicar el
tipo constructor registro al duo formado por los nombres de los campos y sus nombres asociados.
- 17 -
Análisis semántico en procesadores de lenguaje
VAR x: RECORD
p_real: real;
p_imag: real
END;
y: RECORD
p_imag: real;
p_real: real
END;
d) punteros: si T es una expresión de tipo, entonces puntero(T) es una expresión de tipo denominada puntero a un
VAR p: ↑fila
declara una variable p de tipo puntero (fila).
e) funciones: matemáticamente una función es una aplicación entre dos conjuntos, el conjunto inicial o dominio, y el
f: D → R
Por ejemplo la función mod del lenguaje Pascal tiene como dominio integer x integer (un par de enteros) y como rango
A menudo, por razones de implementación, se limitan los tipos que puede devolver una función; por ejemplo, no se
suele permitir que devuelvan arrays o funciones. Sin embargo, existen lenguajes, como el LISP, que permiten a las funciones
es decir g toma como argumento una función que devuelve un entero, y produce como resultado otra función del mismo tipo.
4. Las expresiones de tipo pueden contener variables cuyos valores son también expresiones de tipo.
SISTEMAS DE TIPOS
Un sistema de tipos es un conjunto de reglas para asignar expresiones de tipo a varias partes del programa. La
1 Se supone que x tiene más precedencia que → (es asociativa por la derecha)
- 18 -
Análisis semántico en procesadores de lenguaje
Pueden utilizarse diferentes sistemas de tipos en distintos compiladores de un mismo lenguaje de programación. Por
ejemplo, en Pascal, el tipo array incluye el conjunto de índices del array. Sin embargo, muchos compiladores obligan a especificar
el tamaño del array cuando se pasa como subíndice. Así, estos compiladores usan diferente sistema de tipos que el usado en la
definición del lenguaje. De forma similar, en el sistema operativo UNIX, el comando lint busca en los programas en C los
La comprobación de tipos en tiempo de compilación se denomina estática, mientras que la comprobación en tiempo
En principio, cualquier comprobación puede realizarse dinámicamente, si el código objeto transporta el tipo de un
Un sistema de tipos puro elimina la necesidad de la comprobación dinámica, dado que determina todos los errores en
tiempo de compilación. Es decir, un sistema de tipos puro con tipo "tipo_error" a una zona de un programa, y no permite que
se ejecute éste.
Un lenguaje se dice fuertemente tipeado si su compilador puede garantizar que los programas que acepta pueden
En la práctica, algunas comprobaciones deben realizarse siempre en tiempo de ejecución. Por ejemplo, si se declara:
VAR
vector: ARRAY [0..256] OF char;
i: integer;
y se calcula vector[i], un compilador no puede garantizar, en general, durante la ejecución del programa que el valor de i esté
TRATAMIENTO DE ERRORES
Una vez que se ha detectado un error en la comprobación de tipos, es necesario indicarlo al programador, señalando su
naturaleza y localización.
Sea un lenguaje de programación en el cual es obligatorio declarar todos los identificadores antes de ser usados. Se
pretende construir un comprobador de tipos de este lenguaje, basado en sintetizar el tipo de una expresión a partir del tipo de
sus subexpresiones.
Producciones
- 19 -
Análisis semántico en procesadores de lenguaje
<E> ::= literal | num | id | <E> mod <E> | <E> [ <E> ] | <E>↑
donde
clave: integer;
clave mod 1999
También existe un tercer tipo interno, el tipo_error, usado para indicar errores.
es una expresión de tipo array (1..256,char), donde el constructor de tipos array se ha aplicado al subrango 1..256 y al tipo
char.
Al igual que el lenguaje Pascal, el operador prefijo ↑ en la zona de declaraciones construye un tipo puntero, por ejemplo:
↑integer
es una expresión del tipo puntero(entero), donde el constructor ↑ se aplicó al tipo entero.
* La primera producción ( <P> → <D> ; <E> ) asegura que todos los identificadores sean declarados antes de que
se construyan expresiones.
- 20 -
Análisis semántico en procesadores de lenguaje
* La acción asociada con la producción ( <D> → id : <T> ) almacena el identificador "id" en la tabla de símbolos
añade_tipo(id.entrada, T.tipo)
* Si <T> deriva a char o integer, entonces T.tipo se define como char o integer respectivamente.
* El límite superior de los subíndices del array se obtiene con el atributo val de num.
E2.tipo= integer
then integer
else tipo_error }
else tipo_error }
else tipo_error }
* Se utiliza la función consulta(e) para que busque en la tabla de símbolos el tipo de la entrada e.
A diferencia de las expresiones, las sentencias no son en general de ningún tipo. Se les puede asignar el tipo void (vacío)
En este ejemplo se considerarán las sentencias de asignación, las condicional y la sentencia repetitiva while. Las
secuencias de sentencias se separan por un punto y coma. Para tener en cuenta a las sentencias debe de añadirse a la gramática
de reglas:
- 21 -
Análisis semántico en procesadores de lenguaje
else tipo_error }
else tipo_error }
else tipo_error }
La producción <E> → <E> ( <E> ) representa a una función de un solo argumento, en la cual una expresión es la
Las reglas para asociar expresiones de tipo con no terminales <T> pueden aumentarse con la siguiente producción:
’→’ las comillas son para diferenciar al constructor de funciones del metasímbolo que indica producción.
Desde el punto de vista de la generalización a funciones de más de un argumento, puede indicarse que n argumentos
de tipos T1, T2, ..., Tn pueden verse como un solo argumento de tipo T1xT2xT3x...xTn.
cierto tipo else devuelve tipo_error. Parece conveniente precisar la definición de cuando dos expresiones son equivalentes.
tipos.
Ejemplo:
TYPE MATRIZ= ARRAY [1..10] OF integer;
VAR
a: MATRIZ;
b: ARRAY [1..10] OF integer;
c: ARRAY [1..10] OF integer;
- 22 -
Análisis semántico en procesadores de lenguaje
BEGIN
expresionesEquivalentes(s2,t2)
expresionesEquivalentes(s2,t2)
expresionesEquivalentes(s2,t2)
else
END
tipo de equivalencia puede causar problemas en lenguajes que permiten asignar identificadores a los tipos. Por ejemplo, en el
Sorprendentemente la respuesta depende de la implementación, pues el lenguaje no define cuales son los tipos idénticos.
- 23 -
Análisis semántico en procesadores de lenguaje
lista encadenada puede ser vacía o es un nodo con un puntero que señala a la lista. Estas estructuras de datos se implementan
habitualmente utilizando registros que contienen punteros que señalan a registros similares, y los nombres de los tipos juegan
Ejemplo:
Considérese una lista encadenada de celdas, cada una de ellas contiene una información que es un entero y un puntero
son recursivas.
celda= RECORD
x x
En lenguaje C:
struct celda {
int info;
struct celda *siguiente;
};
x+i
donde:
x: real;
i: integer
En un principio el operador + realiza la adición si ambos operadores son del mismo tipo, en el caso de que no sean del
- 24 -
Análisis semántico en procesadores de lenguaje
En general se convierten los enteros a reales y se realiza la operación real sobre ambos operadores reales.
La comprobación de tipos puede ser utilizada para insertar estas operaciones de conversión en la representación
intermedia del código fuente. Por ejemplo, usando una notación postfija, x+i se convierte en:
x i inttoreal real+
COERCIONES (cast)
Una conversión de un tipo a otro se dice implícita si se realiza automáticamente por el compilador. Las conversiones
Una conversión se dice explícita si el programador debe escribir alguna sentencia, función, ... para realizar la conversión.
Ejemplos:
a) En Pascal, las funciones ord y chr convierten enteros a caracteres y viceversa. Conversión explícita.
b) En C, se convierte implícitamente los caracteres ASCII a enteros entre 0 y 127 en expresiones aritméticas. Coerción.
c) Sean las expresiones formadas al aplicar el operador aritmético op a constantes y operandos según la gramática:
else tipo_error
d) La conversión implícita de constantes se realiza habitualmente en tiempo de compilación, pero también en algunos
casos se realiza en tiempo de ejecución. Por ejemplo, puede teclearse el siguiente código en Pascal:
FOR i:= 1 TO n DO x[i]:= 1;
comprobar su tiempo de compilación y de ejecución.
- 25 -
Análisis semántico en procesadores de lenguaje
Si aumenta el tiempo de compilación, está claro que la conversión automática se realiza en tiempo de compilación. Si
Matemáticas el operador + está sobrecargado, dado que + en A+B tiene diferentes significados en función de que A y B sean
enteros, reales, números complejos o matrices. Otro ejemplo es el paréntesis () en Ada; la expresión A(I) puede ser el I-ésimo
elemento del array A, una llamada a la función A con el argumento I, o una conversión explícita de la expresión I a tipo A.
La sobrecarga se resuelve cuando se determina el significado correcto del símbolo sobrecargado. Por ejemplo en la
expresión:
x + (i + j)
el operador + puede indicar diferentes formas de adición dependiento de los tipos de x, i y j. La resolución de la sobrecarga en
Los operadores aritméticos están sobrecargados en la mayoría de los lenguajes. Sin embargo su resolución se realiza
observando los tipos de los operandos. Las reglas semánticas a utilizar son las mismas que en el caso de <E1> op <E2> (apartado
4.4).
de una función.
Ejemplo:
En Ada, una de las interpretaciones predefinidas del operador "*" es una función de una pareja de enteros a un entero.
Así:
2 * (3 * 5) entonces (3 * 5) → integer
- 26 -
Análisis semántico en procesadores de lenguaje
Hasta ahora se suponía que cada expresión tenía un único tipo, y que la regla para la comprobación de tipos era de la
siguiente forma:
else tipo_error }
Esta regla semántica debe generalizarse al conjunto de posibles tipos de una expresión:
En este caso se supone que la tabla de símbolos puede contener el conjunto de posibles tipos. Puede reservarse el
completa, se pueden reducir los tipos posibles en cada una de las subexpresiones. Si en este proceso no resultase un tipo único
Antes de realizar la descomposición descendente de una expresión en subexpresiones, se pueden observar los conjuntos
de tipos posibles contruidos con las reglas semánticas del apartado anterior. Se muestra que cualquier tipo t ∈ { E.tipos } es un
tipo factible; por ejemplo es posible escoger entre los tipos sobrecargados de los identificadores presentes en la expresión E
de tal forma que E tome el tipo t. Las propiedades asignadas a los identificadores en su declaración hacen que cada elemento
Existen distintos caminos para llegar a que un tipo sea factible. Por ejemplo, considérese la expresión f(x) donde f puede
tener los tipos a→c y b→c, y x puede tener los tipos a y b. Entonces, f(x) tiene tipo c pero x puede ser de tipo a y b.
Parece conveniente por tanto modificar las reglas semánticas de la sección anterior, introduciendo dos nuevos tipos de
atributos único y código.
- 27 -
Análisis semántico en procesadores de lenguaje
else tipo_error
E’.código:= E.código
t:= E.único
E.código:= E1.código ||
E2.código ||
El atributo único indica que sólo se admite este tipo, y se puede propagar de forma descendente de las expresiones a
las subexpresiones.
El atributo código se utiliza para ayudar a la generación de código intermedio.
de tipo fijo. Los procedimientos y funciones polimórficas se pueden ejecutar cada vez con argumentos de diferente tipo. También
El operador & en C se describe como: "si el tipo del operando es ’...’, el tipo de resultado es ’puntero a ...’". Dado que
Este apartado está dirigido a plantear los problemas de la comprobación de tipos para un lenguaje con funciones poli-
mórficas.
- 28 -
Análisis semántico en procesadores de lenguaje
Las funciones polimórficas tienen su principal utilidad en el manejo de estructuras de datos. Por ejemplo, puede ser
conveniente tener una función que determine la longitud de una lista, sin tener que conocer como son los elementos de la lista,
Sin embargo no se ha cumplido nuestro propósito dado que los lenguajes como el Pascal obligan a especificar com-
pletamente el tipo de los parámetros de la función. Así esta función deberá de modificarse para calcular la longitud de una lista
encadenada de reales.
fun longitud(lptr)=
if null(lptr) then 0
else longitud(t1(lptr))+1
longitud(["lunes","martes","miercoles"]);
longitud([10,9,8,7])
BIBLIOGRAFIA
AHO86 Aho A.V. , R. Sethi and J.D. Ullman. Compilers: Principles, techniques, and tools. Addison-Wesley, 1986.
1990.
CAST93 Castro J., Cucker F., Messeguer X., Rubio A., Solano L., Vallés B. Curso de programación. Ed. McGraw-Hill,
1993.
- 29 -
Análisis semántico en procesadores de lenguaje
CUEV91 Cueva Lovelle, J. M. Lenguajes, Gramáticas y Autómatas. Cuaderno Didáctico nº36, Dto. de Matemáticas,
CUEV93a Cueva Lovelle, J. M. Análisis léxico en procesadores de lenguaje. Cuaderno Didáctico nº48, Dto. de
CUEV94 Cueva Lovelle J. M., Mª P. A. García Fuente, B. López Pérez, Mª C. Luengo Díez, y M. Alonso Requejo.
Introducción a la programación estructurada y orientada a objetos con Pascal. Cuaderno Didáctico nº 69,
CUEV94b Cueva Lovelle, J.M. Conceptos básicos de Traductores, Compiladores e Intérpretes. Cuaderno Didáctico nº9,
CUEV95 Cueva Lovelle, J.M. Análisis sintactico en procesadores de lenguaje. Cuaderno Didáctico nº 61, Dto. de
FRAS94 M.D. Fraser, K. Kumar, V.K. Vaishnavi. "Strategies for incorporating formal specifications in software
development". Communications of the ACM, Vol. 37, No. 10, pp. 74-85, October 1994.
HEKM88 Hekmatpour S., Ince D. Software prototyping, formal methods and VDM. Addison-Wesley, 1988.
LEIV93 Leiva Vázquez J.A. y Cueva Lovelle J.M. Construcción de un traductor de lenguaje Eiffel a C++. Cuaderno
TREM85 Tremblay J. P. and P.G. Sorenson. The theory and practice of compiler writing. Ed. McGraw-Hill, 1985.
WATT91 Watt D.A. Programming Language Syntax and Semantics. Prentice-Hall, 1991.
- 30 -
Análisis semántico en procesadores de lenguaje
Tabla de Contenidos
1 INTRODUCCION .................................................................................................................................................. 1
BIBLIOGRAFIA ....................................................................................................................................................... 29
- ii -
Análisis semántico en procesadores de lenguaje
Tabla de figuras
Fig. 1: Arbol sintáctico con atributos ........................................................................................................................ 9
Fig. 1 .......................................................................................................................................................................... 13
Fig. 2 .......................................................................................................................................................................... 15
Fig. 3: Definición recursiva de CELDA .................................................................................................................... 24
- iii -