Análisis semántico
Se compone de un conjunto
de rutinas independientes, llamadas por los analizadores morfológico y sintáctico.
El análisis semántico utiliza como entrada el árbol sintáctico detectado por el análisis sintáctico para comprobar restricciones de tipo y otras limitaciones semánticas y preparar la generación de código.
En compiladores de un solo paso, las llamadas a las rutinas semánticas se realizan directamente desde el analizador sintáctico y son dichas rutinas las que llaman al generador de código. El instrumento más utilizado para conseguirlo es la gramática de atributos.
En compiladores de dos o más pasos, el análisis semántico se realiza independientemente de la generación de código, pasándose información a través de un archivo intermedio, que normalmente contiene información sobre el árbol sintáctico en forma linealizada (para facilitar su manejo y hacer posible su almacenamiento en memoria auxiliar).
En cualquier caso, las rutinas semánticas suelen hacer uso de una pila (la pila semántica) que contiene la información semántica asociada a los operandos (y a veces a los operadores) en forma de registros semánticos.
Propagación de atributos Sea la expresión
int a,b,c;
a/(b+c^2)
El árbol sintáctico es:
/
---------
| |
a +
---------
| |
b ^
---------
| |
c 2
De la instrucción declarativa, la tabla de símbolos y el analizador morfológico obtenemos los atributos de los operandos:
/
---------
| |
a +
int ---------
| |
b ^
int ---------
| |
c 2
int int
Propagando los atributos obtenemos:
/ int
---------
| |
a + int
int ---------
| |
b ^ int
int ---------
| |
c 2
int int
Si la expresión hubiera sido
a/(b+c^−2)
El árbol sintáctico sería el mismo, sustituyendo 2 por −2. Sin embargo, la propagación de atributos sería diferente:
/ real
---------
| |
a + real
int ---------
| |
b ^ real
int ---------
| |
c −2
int int
En algún caso podría llegar a producirse error (p.e. si / representara sólo la división entera).
Si la expresión hubiera sido
int a,b,c,d;
a/(b+c^d)
El árbol sintáctico sería el mismo, sustituyendo 2 por d. Sin embargo, la propagación de atributos sería incompleta:
/ {int,real}
---------
| |
a + {int,real}
int ---------
| |
b ^ {int,real}
int ---------
| |
c d
int int
El analizador semántico podría reducir los tipos inseguros al tipo máximo (real) o utilizar un tipo interno nuevo (ej. arit={int,real}, una unión).
Lo anterior es un ejemplo de propagación bottom-up. La propagación top-down también es posible: lo que se transmite son las restricciones y los tipos de las hojas sirven de comprobación. Por ejemplo, si la división sólo puede ser entera, transmitimos hacia abajo la restricción de que sus operandos sólo pueden ser enteros. Al llegar a d, esa restricción se convierte en que d debe ser positiva. Si no lo es, error.
La implantación de todos los casos posibles de operación con tipos mixtos podría ser excesivamente cara. En su lugar, se parte de operaciones relativamente simples (ej. int+int, real+real) y no se implementan las restantes (ej. int+real, real+int), añadiendo en su lugar operaciones monádicas de cambio de tipo (ej. int→real).
Esta decisión puede introducir ambigüedades. Por ejemplo, sea el programa
real a;
int b,c;
a:=b+c
El árbol sintáctico es:
:=
---------
| |
a +
real ---------
| |
b c
int int
Existen dos conversiones posibles:
:= real := real
--------- ---------
| | | |
a + real a + int
real --------- real ---------
| | | |
b c b c
int int int int
El problema es que no tenemos garantía de que los dos procedimientos sean equivalentes. El segundo puede dar overflow, el primero pérdida de precisión. La definición del lenguaje debe especificar estos casos.
Las transformaciones posibles se pueden representar mediante un grafo cuyos nodos son los tipos de datos y cada arco indica una transformación. Dado un operando de tipo A que se desea convertir al tipo B, se trata de encontrar una cadena de arcos que pase de A a B en el grafo anterior. Podría haber varios grafos, cada uno de los cuales se aplicará en diferentes condiciones, por ejemplo, uno para las asignaciones, otro para las expresiones, etc.