Está en la página 1de 5

Conferencia 6. Relación Sintaxis Semántica.

1. Análisis de tipos.
2. Adecuación de la sintaxis del lenguaje para otras sentencias.

Bibliografía:
- Compilers: pp. 343—360.
- Conferencia en el servidor.

Introducción:
- Recordar la esencia del análisis semántico.
- Recordar los chequeos de tipos hechos en las declaraciones de etiquetas y saltos.

Desarrollo:

1. Análisis de tipos.

Veamos qué sucede cuando en un lenguaje que permite la mezcla de tipos en las expresiones aparecen
diferentes tipos.

Supongamos que tenemos:

i, j: integer;
a, b: real;

La notación polaca de las expresiones:

i := a*b + j;
i := j + a*b;

sería

i, a, b, *, j, +, :=
i, j, a, b, *, +, :=

Veamos que sucedería al ejecutar esta FI

a
b
i

a*b  real
i

j  entero
a*b  real
i

Será necesario convertir el tope de la pila a real.

a*b+j  real
i  entero

Ahora será necesario convertir el tope de la pila de real a entero.


En el segundo ejemplo:

b
a
j
i

Después de la multiplicación tenemos:

a*b  real
j  entero
i

Ahora será necesario convertir el subtope a real.

Evidentemente, este análisis no debe realizarse en ejecución, sino que será necesario realizarse durante el
proceso de generación de la FI. Para ello se deben definir algunos operadores nuevos y modificar algunos
ya existentes.

CERT: convertir de entero a real el tope de la pila


CERS: convertir de entero a real el subtope de la pila
CRE: convertir de real a entero el tope de la pila
+R: suma de reales
+E: suma de enteros
:=R: asignación de reales
...

En base a esto las notaciones polacas de las expresiones anteriores nos quedarían de la forma:

i, a, b, *R, j, CERT, +R, CRE, :=E


i, j, a, b, *R, CERS, +R, CRE, :=E

Una posible forma de generar estas conversiones mediante las rutinas semánticas podría ser mantener
una pila que denominaremos TIPO para almacenar los tipos de los operandos. El apuntador a esta pila
será CAB.

Esto provocaría una revisión total de todos los tipos de todas las rutinas semánticas relativas a la
manipulación de expresiones vistas anteriormente. Así para la regla:

<var>  id
<id-arreglo>  id

además de las acciones anteriores será necesario añadir:

TIPO[++CAB] = TS[p].tipo;

Para la regla:

E  E+T

serán necesarios los cambios siguientes:


if (TIPO[CAB] == "entero")
if (TIPO[CAB-1] == "real") // real-entero
{
POLACA[J++] = CERT;
POLACA[J++] = '+R';
CAB--;
}
else // entero-entero
{
POLACA[J++] = '+E';
CAB--;
}
else
if (TIPO[CAB-1] == "real") // real-real
{
POLACA[J++] = '+R';
CAB--;
}
else // entero-real
{
POLACA[J++] = 'CERS';
POLACA[J++] = '+R';
TIPO[--CAB] = "real";
}

Esta es, pues, la rutina semántica con análisis de tipos para esta regla sintáctica. Para el resto de las
reglas debe hacerse un análisis similar, que queda propuesto como estudio independiente.

Por razones meramente didácticas se ha ilustrado la problemática del análisis de tipos considerando
solamente dos tipos de datos: enteros y reales. Si el lenguaje contiene otros tipos de datos, que pueden
mezclarse en las expresiones aritméticas –como sucede en la práctica la mayoría de las veces–, entonces
es necesario contemplar otras posibles combinaciones de tipos en las rutinas semánticas.

2. Adecuación de la sintaxis del lenguaje para otras sentencias.

Analicemos la forma interna en notación polaca de las sentencias siguientes:

1) La sentencia times:

times <Expr> do <Sent>

Forma interna:

R(<Expr>), SLE0, , R(<Sent>), DEC, SI, , POP,

DEC:
PILA[TOP]--;
ACT++;

SLE0:
if (PILA[TOP] <= 0)
ACT = POLACA[ACT+1];
else
ACT += 2;
POP:
TOP--;
ACT++;

Para poder generar esta forma interna necesitamos modificar la gramática que define esta sentencia,
puesto que se necesita generar código entre los no terminales <Expr> y <Sent>. Como se requiere
generar código en dos lugares de la forma interna, necesitamos para esta sentencia al menos dos reglas
de producción, a las cuales asociar las rutinas semánticas que generen este código. Así, podemos utilizar,
por ejemplo, las siguientes reglas de producción:

(1) <Sent-Times>  <Parte-Expr> do <Sent>


(2) <Parte-Expr>  times <Expr>

Las rutinas semánticas asociadas a cada regla serían:

(2):
if (TIPO[CAB--] != "entero") {
error("El tipo de la expresión debe ser entero.");
// Eventualmente podrían tomarse aquí decisiones de conversión de tipos.
// Por ejemplo, si el tipo de la expresión es real, puede convertirse a entero.
}
POLACA[J++] = 'SLE0';
SEM[++T] = J++;

(1):
POLACA[J++] = 'DEC';
POLACA[J++] = 'SI';
POLACA[J++] = SEM[T]-1;
POLACA[J] = 'POP';
POLACA[SEM[T--]] = J++;

2) La sentencia while:

while <Cond> do <Sent>

Forma interna:

R(<Cond>), SSF, , R(<Sent>), SI, , ,

Para poder generar esta forma interna es necesario modificar la gramática que define la sentencia, puesto
que necesitamos generar código en tres lugares diferentes: antes de la condición (para guardar la
posición donde comienza su código), entre los no terminales <Cond> y <Sent> (para generar el SSF), y
después de la sentencia (para generar el SI, y resolver el SSF). Necesitamos, por lo tanto, al menos tres
reglas de producción para generar este código. Estas reglas pueden ser, por ejemplo:

(1) <Sent-While>  <Parte-Cond> do <Sent>


(2) <Parte-Cond>  <While> <Cond>
(3) <While>  while

Las rutinas semánticas asociadas a cada regla serían:

(3):
SEM[++T] = J;

(2):
if (TIPO[CAB--] != "boolean") {
error("El tipo de la condición debe ser boolean.");
// Eventualmente podrían tomarse aquí decisiones de conversión de tipos.
// Por ejemplo, si el tipo de la condición es numérico, puede convertirse a boolean.
}
POLACA[J++] = 'SSF';
SEM[++T] = J++;

(1):
POLACA[J++] = 'SI';
POLACA[J++] = SEM[T-1];
POLACA[SEM[T]] = J;
T -= 2;

3) La sentencia repeat:

repeat <ListaSent> until <Cond>

Forma interna:

R(<ListaSent>), R(<Cond>), SSF, ,

Para poder generar esta forma interna también es necesario modificar la gramática que define la
sentencia, puesto que antes de generar el código de la lista de sentencias necesitamos guardar la
dirección de donde comienza éste, para luego hacer el SSF a este lugar. Como deben realizarse acciones
semánticas en dos partes diferentes de la forma interna, necesitamos al menos dos reglas de producción
para generar este código. Estas reglas pueden ser, por ejemplo:

(1) <Sent-Repeat>  <Repeat> <ListaSent> until <Cond>


(2) <Repeat>  repeat

Las rutinas semánticas asociadas a cada regla serían:

(2):
SEM[++T] = J;

(1):
if (TIPO[CAB--] != "boolean") {
error("El tipo de la condición debe ser boolean.");
// Eventualmente podrían tomarse aquí decisiones de conversión de tipos.
// Por ejemplo, si el tipo de la condición es numérico, puede convertirse a boolean.
}
POLACA[J++] = 'SSF';
POLACA[J++] = SEM[T--];

También podría gustarte