• No se han encontrado resultados

Diseño y Construcción de Compiladores

N/A
N/A
Protected

Academic year: 2020

Share "Diseño y Construcción de Compiladores"

Copied!
29
0
0

Texto completo

(1)

Teoría: Análisis Semántico

Diseño y Construcción de Compiladores

Licenciatura en Ciencias de la

Computación

Profesora Responsable: Dra. Victoria Aragón Profesora Colaboradora: Lic. Susana Esquivel Jefe de Trabajos Prácticos: Dr. Edgardo Ferretti Auxiliar de Práctico: Lic. María José Garciarena Ucelay

https://sites.google.com/site/ddcompilersunsl/

Diseño y Construcción de Compiladores UNSL

(2)

ANÁLISIS SEMÁNTICO

Tarea Fundamental del Análisis

Semántico: garantizar que el programa “tiene

sentido”.

Objetivo: Asegurar que el tipo de una construcción

(3)

ANÁLISIS SEMÁNTICO

Estático: todos los controles pueden

efectuarse en tiempo de compilación.

Análisis Semántico

Dinámico: los controles se realizan en

(4)

ANÁLISIS SEMÁNTICO ESTÁTICO

El análisis semántico que se realiza en tiempo de compilación puede involucrar distintas actividades, dependiendo de las características del lenguaje de programación. Estas actividades pueden ser:

Chequeo de tipos: por ejemplo, un compilador deberá reportar un

error si un operador se aplica a un operando incompatible. Por ejemplo en C el operador % (módulo) requiere operandos de tipo entero.

Chequeo del flujo de control: cuando existen sentencias que

causan que el flujo de control deje una construcción, en este caso se debe controlar que exista algún lugar del programa al cual transferir el control, por ejemplo, en C una sentencia break causa que el control deje un while, un for, un switch.

(5)

ANÁLISIS SEMÁNTICO ESTÁTICO

Chequeos de Unicidad: Existen característica en las que un objeto

computacional debe definirse exactamente un sola vez por ej. En PACAL un identificador dentro de un bloque (procedimiento/función) debe ser definido una única vez, los rótulos de la sentencia CASE deben ser distintos, los elementos de un tipo escalar no deben estar repetidos.

Chequeos de Nombres: En algunos lenguajes un mismo nombre

puede aparecer dos o más veces. Por ejemplo, en ADA un ciclo o un bloque puede tener un nombre al principio y al final de la construcción, el compilador debe chequear que el mismo nombre es usado en ambos lugares.

(6)

CHEQUEO DE TIPOS

Objetivo: Asegurar que el tipo de una construcción del

programa coincida con el previsto en sus contexto.

Controles Habituales:

Declaración de identificadores antes de su uso.

Compatibilidad de tipos en las asignaciones.

Operadores aplicados a datos del tipo adecuado.

N° y tipo correcto de parámetros en la invocación de

(7)

CHEQUEO DE TIPOS

Además se debe considerar:

CONVERSIÓN DE TIPOS (cuando estos sean compatibles y

el lenguaje lo admita)

• Conversión IMPLÍCITA (coerciones) → realizada por el

compilador automáticamente.

• Conversión EXPLÍCITA → realizada por el programador

(cast en C).

(8)

CHEQUEO DE TIPOS

SOBRECARGA DE OPERADORES

• Símbolos del lenguaje que tienen asociados

distintos tipos y operaciones en función del

contexto.

Ejemplo

suma de enteros

Operador „+‟ suma de reales

(9)

CHEQUEO DE TIPOS

Para poder realizar el chequeo de tipos el compilador necesita:

Un sistema de tipos (contexto): que especifica el conjunto de

reglas que permite asociar tipos con las distintas construcciones de un lenguaje (que está definido por el diseñador del lenguaje) para poder verificar su corrección.

Un conjunto de construcciones sintácticas que define el

lenguaje y con las cuales se asociaran los tipos según el sistema de tipos.

Un conjunto de expresiones de tipo: los tipos tienen una

estructura que representaremos usando expresiones de tipo, donde

una expresión de tipo es un tipo asociado con una construcción del lenguaje.

(10)

EXPRESIONES DE TIPO

DEFINICIÓN

Una expresión de tipo es o bien un tipo básico, o es el resultado de aplicar un constructor de tipo a otra expresión de tipo de acuerdo a unas reglas de construcción (dadas por el lenguaje).

Tipos básicos: integer, char, bool, float, …. Tipos básicos especiales:

error_tipo: Indica que no se puede asociar una expresión

de tipo correcta con la construcción analizada.

void: Indica que una construcción del lenguaje es correcta,

pero no tiene ningún tipo asociado. Útil en la comprobación de sentencias.

Un nombre de tipo es una expresión de tipo.

(11)

EXPRESIONES DE TIPO

DEFINICIÓN

Un constructor de tipo permite construir tipos complejos a partir de tipos mas simples.

Un constructor de tipo aplicado a una expresión de tipo es una expresión de tipo. Los constructores incluyen, entre otros:

Constructor de arreglo: Si T es una expresión de tipo entonces

array(I, T) es una expresión de tipo.

(PASCAL) var a: array[0..9] of integer

array(0..9, integer) (C) int a [10]

(12)

EXPRESIONES DE TIPO

DEFINICIÓN

Constructor de producto cartesiano:

Si T

1

y T

2

son expresiones de tipo entonces su producto

cartesiano T

1

x T

2

es una expresión de tipo. Un objeto de este

tipo es una dupla ordenada, con el primer elemento de tipo T

1

y el segundo de tipo T

2

, se supone que „x‟ es asociativa a

izquierda.

(13)

EXPRESIONES DE TIPO

DEFINICIÓN

Constructor de registro: Similar a productos, pero con

nombre asociados a los campos.

Si u

1

, u

2

, . . . , un son los nombre de los campos de tipos T

1

,

T

2

, . . , T

n

, la expresión de tipo asociada al registro es

record((u

1

xT

1

) x (u

2

x T

2

) × . . . × (u

n

x T

n

))

Ej.

struct { record begin

char nombre[10]; nombre: array [0..9] of character; float altura; altura: real;

} end;

La correspondiente expresión de tipo es:

(14)

EXPRESIONES DE TIPO

DEFINICIÓN

Constructor de punteros: Sea T una expresión de tipo entonces pointer(T)

es una expresión de tipo.

var p: ↑ char;

pointer(char)

char * p

Constructor de funciones: Siendo D la expresión de tipo de los

argumentos de la función y T es la expresión de tipo del valor devuelto, la

expresión de tipo de la función es:

D → T

Para funciones con múltiples argumentos se usan productos

float ∗ f(char a, char b);

char x char → pointer(real)

(15)

EQUIVALENCIA DE TIPOS

Objetivo: Determinar si dos expresiones de tipo

se consideran equivalentes.

Equivalencia estructural:

Dos

expresiones

de

tipo

son

equivalentes

estructuralmente si son el mismo tipo básico, o si

están formadas por la aplicación del mismo

constructor de tipos a tipos estructuralmente

equivalentes

(16)

EQUIVALENCIA DE TIPOS

Ejemplo en PASCAL:

Type t_vector : array [0..9] of integer; array(0..9, integer)

t_matriz : array [0..9] of array [0..9] of integer; array(0..9, array(0..9,integer))

Var

uno: array [0..9] of integer;

dos: array [0..9, 0..9] of integer; Construir expresiones de tipo tres: t_matriz;

cuatro: array [0..9] of t_vector; cinco: array [0..9] of t_vector;

(17)

EQUIVALENCIA DE TIPOS

Ejemplo en PASCAL:

Type t_vector : array [0..9] of integer; array(0..9, integer)

t_matriz : array [0..9] of array [0..9] of integer; array(0..9, array(0..9,integer))

Var

uno: array [0..9] of integer; array(0..9, integer)

dos: array [0..9, 0..9] of integer; array(0..9, array(0..9, integer)) tres: t_matriz; array(0..9, array(0..9, integer)) cuatro: array [0..9] of t_vector; array(0..9, array(0..9, integer)) cinco: array [0..9] of t_vector; array(0..9, array(0..9, integer))

(18)

EQUIVALENCIA DE TIPOS

Equivalencia nominal o por nombre

Dos expresiones de tipo son nominalmente equivalentes si son el mismo tipo básico o si a ambas se les ha asociado el mismo nombre. (Semejante a la estructural pero sin reemplazar los nombres por la expresión de tipo asociada al nombre definido por el usuario)

Type t_vector : array [0..9] of integer; array(0..9, integer)

t_matriz : array [0..9] of array [0..9] of integer; array(0..9, array(0..9,integer))

var

uno: array [0..9] of integer; array(0..9, integer) dos: array [0..9, 0..9] of integer; array(0..9, array(0..9, integer)) tres: t_matriz; array(0..9, array(0..9, integer)

cuatro: array [0..9] of t_vector; array(0..9, t_vector) cinco: array [0..9] of t_vector; array(0..9, t_vector)

(19)

EQUIVALENCIA DE TIPOS

OTRO EJEMPLO:

typedef entero int; int

entero* a; pointer(int)

int *b; pointer(int)

¿ Son estructuralmente equivalentes? SI

¿ Son nominalmente equivalentes? NO

(20)

VERIFICACIÓN DE LA

EQUIVALENCIA ESTRUCTURAL

function esEquivalente(S, T): boolean begin

if S and T son el mismo tipo básico then return TRUE;

else if (S = array(S1, S2)) and (T = array(T1, T2))

then return [esEquivalente(S1,T1) and esEquivalente(S2,T2)]; else if (S = S1 × S2) and (T = T1 × T2)

then return [esEquivalente(S1,T1) and esEquivalente(S2,T2)]; else if (S = pointer(S1)) and (T = pointer(T1))

then return [esEquivalente(S1,T1)]; else if (S = S1 → S2) and (T = T1 → T2)

then return [esEquivalente(S1,T1) and esEquivalente(S2,T2)] else return FALSE;

End

NOTA: LA EQUIVALENCIA NOMINAL es más fácil de verificar, basta con comprobar que coinciden los nombres asociados con las expresiones de tipo.

(21)

CONVERSIÓN DE TIPOS

Tipos ≠ → Distintas instrucciones.

Distinta representación en memoria.

Conversión implícita (coerción):

• Se realizan automáticamente.

• No se puede perder información.

• Coerción de constantes.

Conversión explícita:

• Especificada por el programador.

• Como llamadas a funciones.

(22)

CONSTRUCCIÓN DE UN CHEQUEADOR

DE TIPOS USANDO ETDS

EJEMPLO: Sea la siguiente gramática

P → SD ; SS. SD → D ; SD1 | D D → T : LI

T → int | bool | stack(T) LI → id , LI1 | id

SS → S ; SS1

S → AS | REP | PUSH(id, EX) AS → id:= EX

REP → while EX do SS od EX → TE + EX1 | TE

(23)

EJEMPLO (CONTINUACIÓN)

Con el siguiente sistema de tipos:

Todo identificador debe ser declarado antes de su uso.El stack sólo puede contener elementos del mismo tipo.Una cte_num es de tipo entero.

 Una constante con valor FALSE o TRUE es de tipo lógico.  En la asignación no se admiten coerciones.

La expresión en la sentencia while debe ser booleana.

En la sentencia PUSH el identificador debe ser de tipo stack y la

expresión debe ser del tipo del elemento con el cual fue declarado el stack.

En la función POP el identificador debe ser de tipo stack y devuelve como resultado el elemento que se encuentra en el tope del stack.

El operador + está sobrecargado, si los dos operandos son enteros entonces representa la suma aritmética, si ambos operandos son de tipo lógico representa la operación AND, en otro caso error.  Una sentencia tiene tipo void.

(24)

Esquema de Traducción

En el ETDS se usa:

Un atributo sintetizado para el tipo de las construcciones del lenguaje, de tipo string, su nombre es <noterminal>.tipos

Un atributo heredado para asociar el tipo con cada identificador en una declaración múltiple, es de tipo string y su nombre <noterminal>.tipoh

La función addTS ingresa información de los identificadores en la TS devolviendo un TRUE si lo pudo insertar y un FALSE si ya existía. Las variables deben ser declaradas antes de su uso. Considere, de acuerdo a su criterio, las acciones a llevar a cabo respecto a los identificadores duplicados.

La función searchTS recupera información de la tabla de símbolos, devolviendo el atributo tipo, si existe o un string “” caso contrario.

Considere, de acuerdo a su criterio, las acciones a llevar a cabo respecto a los identificadores no definidos.

(25)

ETDS para la declaraciones: Todo identificador debe ser

declarado antes de su uso.

SD → D ; SD1 {SD.tipos= SD1.tipos} |

D {SD.tipo = D.tipos}

D → T : {LI.tipoh = T.tipos} LI {D.tipos=void}

LI → id {if ! addTS(id.entry, LI.tipoh) then error_tipo},

{LI1.tipoh = LI.tipoh} LI1 |

id {if ! addTS(id.entry, LI.tipoh) then error_tipo}

T → int {T.tipos = integer} | bool {T.tipos= bool} | constructor de stack

(26)

ETDS para: el stack sólo puede contener elementos

del mismo tipo y las sentencias tienen tipo void.

S → PUSH(id, EX)

{aux= searchTS(id.entry, tipo);

if aux == “” then error_tipo (1) else {t= tipo_base(aux); if

aux != stack(t) || t!=EX.tipos then error_tipo (2); } S.tipos =

void}

En (1) se debe tratar el error de identificador no definido.

En (2) se debe tratar el error de que el elemento que se

quiere insertar en el stack no coincide con el tipo de los

elementos del stack.

tipo_base(x) retorna el tipo base de un constructor de stack.

Notar que independientemente de que haya o no existido

error la sentencia lleva tipo void

(27)

ETDS para: controlar los términos (TE)

Una cte_num es de tipo entero.

Una constante con valor FALSE o TRUE es de tipo lógico.

TE → cte_num {TE.tipos= integer}|

TRUE {TE.tipos = bool } |

FALSE {TE.tipos = bool}

Todo identificador debe ser declarado antes de su uso (uso del identificador).

TE → id {aux=searchTS(id.entry, tipo);

if aux ==“” then error_tipo else TE.tipos = aux}

En la función POP el identificador debe ser de tipo stack y devuelve como resultado el elemento que se encuentra en el tope del stack.

TE → POP(id) {aux =searchTS(id.entry, tipo) ;

if aux ==“” then error_tipo else {t= tipo_base(aux);

(28)

ETDS para: las sentencias

S

→ AS

{S.tipos=void}

| REP

{S.tipos =void}

AS

→ id:= EX

{aux = searchTS(id.entry, tipo); if

aux == “” then tipo_error else if aux !=Ex.tipos then

error_tipo; AS.tipos= void}

REP

→ while EX do SS od

{if EX.tipos != bool then

tipo_error; REP.tipos=void}

(29)

ETDS para Expresiones

El operador + está sobrecargado, si los dos operandos son enteros entonces representa la suma aritmética, si ambos operandos son de tipo lógico representa la operación AND, en otro caso error (esto se debe tener en cuenta en la generación de código)

EX → TE+EX1 {if TE.tipos == int ^ EX1.tipos== int then

EX.tipos = TE.tipos else if TE.tipos == bool ^ EX1.tipos ==

bool then EX.tipos = TE.tipos else error_tipo} | TE {EX.tipos = TE.tipos}

O

EX → TE+EX1 {if TE.tipos == EX1.tipos then EX.tipos =

Referencias

Documento similar

En nuestra opinión, las cuentas anuales de la Entidad Pública Empresarial Red.es correspondientes al ejercicio 2010 representan en todos los aspectos significativos la imagen fiel

En nuestra opinión, las cuentas anuales de la Entidad Pública Empresarial Red.es correspondientes al ejercicio 2012 representan en todos los aspectos

La Intervención General de la Administración del Estado, a través de la Oficina Nacional de Auditoría, en uso de las competencias que le atribuye el artículo 168

La Intervención General de la Administración del Estado, a través de la Oficina Nacional de Auditoría, en uso de las competencias que le atribuye el artículo

Petición de decisión prejudicial — Cour constitutionnelle (Bélgica) — Validez del artículo 5, apartado 2, de la Directiva 2004/113/CE del Consejo, de 13 de diciembre de 2004, por

La invalidez en el MMPI por no respuestas no se considera criterio positivo (sólo se puede considerar tal posibilidad en caso de daño neurológico que justifique tal estilo

1. LAS GARANTÍAS CONSTITUCIONALES.—2. C) La reforma constitucional de 1994. D) Las tres etapas del amparo argentino. F) Las vías previas al amparo. H) La acción es judicial en

El contar con el financiamiento institucional a través de las cátedras ha significado para los grupos de profesores, el poder centrarse en estudios sobre áreas de interés