19/09/2013
UNIDAD I.- ANALISIS SEMANTICO
2
1.1.- Arboles de expresiones. 1.2.- Acciones semánticas de un analizador sintáctico. 1.3.- Comprobaciones de tipos en expresiones . 1.4.- Pila semántica en un analizador sintáctico. 1.5.- Esquema de traducción. 1.6.- Generación de la tabla de símbolo y de direcciones. 1.7.- Manejo de errores semánticos.
MATERIA: MA TERIA: LENGUAJES L ENGUAJES Y AUTÓMA AUTÓM ATAS II Dra. Miriam Martínez Arroyo
Introducción
4
• El
lenguaje es un vehículo por el cual se transmiten instrucciones a un procesador para que las ejecute y produzca ciertos resultados.
• Es
tarea del compilador extraer el contenido semántico incluido en las sentencias del programa.
• Ciertos
INTRODUCCIÓN
El compilador hasta ahora
3
5
aspectos relativos a la corrección de un programa no se pueden expresar claramente mediante el lenguaje de programación.
• Es
necesario dotar al compilador de de rutinas auxiliares para captar todo lo que no se ha expresado mediante la sintaxis del lenguaje.
Introducción
6
• Análisis léxico • Detecta entradas de tokens legales
• Semántica: conjunto conjunto de reglas que espe signi significado ficado de cual cualquier quier
• Análisis Sintáctico • Detecta entradas con árboles mal formados.
• El
• Análisis Semántico • Detecta todos los errores restantes
sentencia sintácticamen sentencia sintácticamente te lenguaje.
correcta correc ta y escrit escrita a en un determinado determinado
análisis semántico, a diferencia de otras no se realiza claramente diferenciado del resto de las tareas del compilador. • – Fase
en la que se obtiene información necesaria para la compilación, tras conocer la estructura sintácticadel programa. • – Completa las fases de análisis léxico y sintáctic sintácticoo incorporando comprobaciones comprobaciones que no pueden asimilarse al mero reconocimiento de una cadena dentro de un lenguaje
1
19/09/2013
Objetivo del análisis semántico
7
Objetivo del análisis semántico
8
Algunas de estas comprobaciones son: • La
semánt sem ántica ica del len lengua guaje je for forma ma par parte te de la esp especi ecific ficaci ación ón del mismo. Normalmente la Normalmente la semántica de un lenguaje se describe de manera informal. informal.
•
es comprobar objetivo del análisis semántico es comprobar si la semántica del programa que se está compilando cumple las especificaciones de la semántica del lenguaje fuente. fuente .
• El
Por qué nec necesi esita tamos mos el aná anális lisis is sem semánt ántico ico
Para rea Para realiz lizar ar el aná anális lisis is sem semánt ántico ico se uti utiliz lizan an gramáticas gramáticas de básicam icament ente, e, son gra gramát mática icass ind indepe ependie ndientes ntes del atributos,, que bás atributos contexto en las que los símbolos tienen asignados atributos o valores semánticos.
Hay dos tipos de atributos: • sintetizados:
corresponden al no terminal de la parte izquierda de una regla. Se calculan a partir de los atributos de los símbolos de la parte derecha de la regla. • heredados: corresponden a los símbolos de la parte derecha de la regla. Se construyen a partir de los atributos de los símbolos de ambas partes de la regla.
9
¿Qué hace el análisis semántico?
10
• Verificaciones de varias clases, típicamente: • Que todos los identificadores estén declarados. • Los tipos de las expresiones y la compatibilidad de las asignaciones. • Invocación de métodos compatibles con las declara ciones
• El analizador sintáctico no puede detectar todos los errores. • Algunos constructores del lenguaje no son libres de contexto • Ejemplo: declaración de identificadores y su uso. • No pued puedes es utiliz utilizar ar una GLC para descr describir ibir que algun algunaa palab palabra ra particulares aparezca dos veces en una cadena separada por un texto en medio. • Una versión abstracta del problema es: {wcw | w (a | b)* }
• etc … • Los requerimientos
dependes del lenguaje.
declaración
uso
• Se
asocia información a las construcciones del lenguaje de programación proporcionando atributos a los símbolos de la gramática.
Ejemplo: • el valor de una expresión, • el tipo de una variable, • su ámbito, • un trozo de código, • el número de argumentos de una función, • etc. Los valores de los atributos se calcu calculan lan mediante reglas semánticas semánticas asociadas a las producciones gramaticales.
El análisis semántico incluye:
11
• La
construcción de la Tabla de Símbolos para llevar un seguimiento del significado de los identificadores en el programa:
12
• variables, • funciones, • tipos, • parámetros y • métodos de paso de parámetros en funciones, • Etc. •
• Real Realizar izar
la com compr proba obació ciónn e inf infere erenci nciaa de tip tipos os en exp expres resion iones es y sentencias. Por ejemplo: • que ambos lados de una asignación tengan tipos adecuados, • que no se declaren variables con el mismo nombre, • que los parámetros de llamada a una función tengan tipos adecuados, • número de parámetros correcto, correcto, • Etc.
2
19/09/2013
14
1.1.-ARBOL DE EXPRESIONES
1.1.- Arboles de expresiones.
13
Las expresiones regulares que se pueden representar a través de los símbolos contenidos en un alfabeto, también se pueden representar a través de árboles, que se denominan árboles de expresiones. La información contenida en los árboles de expresio-nes tienen las siguientes características. Se almacena en forma de estructura jerárquica, porque los componentes están en diferentes niveles.
La información se almacena en forma Dinámica porque su forma, tamaño y contenido pueden variar durante la ejecución.
15
16
Los árboles de expresiones están compuestos por los siguientes elementos:
Los datos son almacenados de forma Organizada porque importa la forma en que está dispuesto el contenido.
Token.- es una Unidad mínima de información entendible por un compilador. Puede ser un ope-rando o un operador. Operandos.- Los operandos pueden ser las hojas de los nodos. Raíz.- La raíz de un árbol puede ser cuaquier operador aritmético.
La consulta de los datos se hace de manera rápida y eficiente obteniendo mejores resultados.
Reglas para un árbol
Propiedades de los árboles
17
La reglas para representar una expresión mediante un árbol son las siguientes: 1.- Cada hoja está etiquetada con un operando y solo consta de ese operando.
Árboles de expresión
18
Representan un orden de ejecución para la expresión siguiente:
+
A
2.- Cada nodo interior “n” está etiquetado con un solo operador.
+
*
(A* B) + C * D + E
B
E
* C
D
3.- Las hojas están representadas por los operandos y los nodos por la raíz de cada árbol.
3
19/09/2013
Otro ejemplo de árbol sintáctico para una expresión Aritmética: (7 + 12) * (-9)
19
20
21
22
23
24
-171
* +
7
-
12
9
Ejemplo: x=(x+y)*(a-b)
4
19/09/2013
25
26
27
28
29
30
5
19/09/2013
31
32
http://www.esdebian.org/wiki/interpretprete-funciones-mediante-arbol-binario-expresiones
33
34
35
36
6
19/09/2013
37
38
1.2. Acciones Semánticas de un Analizador Sintáctico
40
Dependiendo del tipo de sentencias, las acciones semánticas pueden agruparse en:
1.2. Acciones Semánticas de un Analizador Sintáctico
39
• Sentencias de Declaración : Se utilizan para completar la sección de tipos de la Tabla de Símbolos. • Sentencias “ejecutables”: Se utilizan para realizar
comprobaciones de tipos entre los operandos implicados.
Algunas de estas comprobaciones son: • comprobación
de tipos en sentencias de asignación de tipos en operaciones aritmético-lógicas • comprobación de tipos en las sentencias condicionales • comprobación de la declaración de las variables antes de su uso • comprobación de unicidad de identificadores • comprobación del indexado de vectores • comprobación
Continuación…
41
y procedimientos: Se utilizan para comprobar el número, orden y tipo de los parámetros actuales en cada llamada a una función o procedimiento.
• Funciones
42
de variables: Se utilizan para comprobar si un identificador ha sido declarado antes de utilizarlo. • Identificación
• Etiquetas: comprobar si hay etiquetas repetidas y
validación.
7
19/09/2013
Continuación… • Constantes:
comprobar que no se utilicen en la parte izquierda de una asignación.
43
• Sobrecarga de operadores y funciones: en las
funciones y los procedimientos, se utilizan para detectar y solventar la autenticidad de los datos.
45
• La mayoría de los lenguajes tienen ámbitos estáticos. • El ámbito sólo depende del texto del programa no de la conducta en tiempo de ejecución. • Java, C, C++, Pascal, Modula, cool, etc. Tienen ámbito estático. • Pocos lenguajes tiene ámbitos dinámicos • Lisp, SNOBOL • El ámbito depende de la ejecución del programa
• No
todas las clases de identificadores siguen la regla del anidamiento más cercano • Por ejemplo las definiciones de clases • Todas son visibles globalmente • Declaración de campos en Java • Visibles a todos los métodos en una clase • Y algunas veces a otras clases también • Dependiendo
si son públicas o privadas, etc.
44
en empatar las declaraciones de identificadores con su uso • El ámbito de un identificador es la porción del programa en la cual el identificador es accesible. • El mismo identificador se puede referir a diferentes cosas en diferentes partes del programa. • Ámbitos diferentes para el mismo nombre no se sobreponen. • Un identificador puede tener ámbitos restringidos. • Es decir solo es visible en áreas particulares del programa
verificación de las constantes para que éstas sean de un mismo tipo.
Ámbito en lenguajes OO
Ámbitos • Consiste
• Conversiones y equivalencias de tipo: realizan la
Ámbitos estáticos vs dinámicos
Ejemplo de ámbito estático
46
int x; -- variable global o campo estático float convert_speed(float y ) { float x; --una x diferente (variable local) x=y*1.6 return x; } (El uso de x se refiere a la definición más cercana )
47
Más sobre ámbito
48
Los nombres de métodos y atributos tienen reglas complejas. • Los nombres de campos son globales dentro de cualquier clase. • Pero los métodos y campos no necesitan estar definidos en la clase en la cual se están utilizando, pero si en una clase padre (herencia) • Los métodos se pueden redefinir (sobreescritura) •
8
19/09/2013
Implementación de la regla anidada más cercana
49
• La
mayor parte del análisis semántico se puede expresar como un recorrido descendente recursivo de un árbol o AST. • Procesa un nodo n • Procesa los hijos de n • Finaliza procesando el
1.3.- Comprobaciones de Tipos en Expresiones
50
nodo n
• En
cualquier porción del árbol (contexto en el programa), necesitamos saber que identificadores están definidos.
1.3.-Comprobación de tipos en Expresiones
51
comprobación de tipos es una forma de asegurar que los identificadores relacionados sean de tipos compatibles.
Las comprobaciones de consistencia que se efectúan antes de la ejecución del programa fuente, se denominan comprobaciones estáticas.
• La
Las comprobaciones que se realizan durante la ejecución del programa objeto se denominan comprobaciones dinámicas.
• Dos
•
La revisión de la sintaxis de un programa fuente es un ejemplo de comprobación estática ,
•
mientras que la comprobación de tipos , es un ejemplo de comprobación que con frecuencia puede efectuarse en forma estática y que en ocasiones debe realizarse dinámicamente.
identificadores son compatibles de acuerdo a lo siguiente: • Cuando forman el lado izquierdo y el lado derecho de un operador. • Cuando forman el lado izquierdo y el lado derecho de una proposición
asignación. sean parámetros reales y formales.
de
• Cuando
Tipos • ¿Qué es un tipo? • La noción varía de lenguaje a lenguaje. • Consenso • Un conjunto de valores • Un conjunto de operadores sobre los valores • Las
53
Tipos y operaciones
52
54
• Ciertas Operaciones son legales para cada tipo • No tiene sentido sumar un apuntador a función y un entero en C • Tiene sentido sumar dos enteros • Pero ambos tienen la misma implementación en lenguaje ensamblador!
clases son una instanciación moderna de la noción de tipo
9
19/09/2013
Sistema de tipos • Un
sistema de tipos de un lenguaje especifica las operaciones que son válidas para cada tipo.
55
mayoría de los compiladores son de múltiples pasadas. el AST (árbol de análisis sintáctico) para el análisis semántico, verificación de tipos. • Recorre este otra vez para optimización. • y generación de código • .. etc.
meta de la verificación de tipos es asegurar que las operaciones se utilizan con los tipos correctos.
• Recorre
• Hace cumplir la interpretación de los valores. • Algunas veces puede realizar conversiones automáticas cuando el lenguaje
permite modos mezclados y promoción de tipos.
• El
sistema de tipos provee una formalización concisa de las reglas de verificación semántica.
Compilación de una pasada Bajo ciertas circunstancias, es posible construir un compilador completo de una sola pasada.
Verificación de tipos 57
aspectos a considerar en la verificación de tipos de un compilador
58
símbolos.
• Esto
requiere que la traslación se realice durante un recorrido del árbol en profundidad.
• Realizar
la verificación de tipos y hacer cumplir las reglas semánticas en expresiones y otros elementos del lenguaje (e.g. Lista de argumentos, etc.)
• La
forma en que el analizador sintáctico se mueve a través del árbol
Verificación de tipos con aseveraciones cada regla declaramos la aseveración que se debe cumplir si el programa es válido.
• Existen
• Procesar las declaraciones y mantener una tabla de símbolos. • Almacenar el tipo de cada identificador en la tabla de
• Es posible con ciertas condiciones del lenguaje • Particularmente cuando se declara antes de que se use.
• En
56
• La
• La
•
Traslación dirigida por la sintaxis
59
• Buscar
el tipo de los identificadores usados.
• Inferir
los tipos de constantes
Ejemplo de verificación de tipos (un ejemplo similar esta en la Sec. 6.4.4 de Kenneth)
60
• ASSERT(condición) ¨mensaje¨ significa: • La condición se supone debe cumplirse • Si no es verdadera, imprime mensaje. • Assert (Visual Studio 2012 ) • Prueba una aserción de software en tiempo de compilación. Si la expresión constante
especificada es false, el compilador muestra el mensaje especificado y la compilación emite un error; si no, la declaración no tiene ningún efecto.
static_assert( constant-expression, string-literal );
• Puede
usarse una macro en C para este propósito
#define ASSERT(x,y) if (!x) printf(¨line %d: %s\n¨,lineno, (y))
10
19/09/2013
Verificación de tipos en Bison
Expresiones de Tipos
61
63
tipo de una construcción de un lenguaje se denotará mediante una “Expresión de tipo”.
• Una
expresión de tipo es, o bien un tipo básico o se forma aplicando un operador llamado constructor de tipos a otras expresiones de tipos.
utilizará la siguiente definición de expresiones de tipos:
64
2- Como se puede dar nombre a las expresiones de tipos, el nombre de un tipo es una expresión de tipo.
Los conjuntos de tipos y construcciones básicas dependen del lenguaje que deba comprobarse.
3.-Un constructor de tipos aplicado a expresiones de tipos es una expresión de tipo. Los constructores incluyen: • Matrices, • Productos, • Registros, • apuntadores • funciones).
65 y
a) Matrices. Si T es una expresión de tipo, entonces array (I, T) es una
expresión de tipo que indica el tipo de una matriz con elementos de tipo T y conjunto de índices I, I con frecuencia es un rango de enteros.
•
• Se
62
1. Un tipo básico es una expresión de tipo. Entre los tipos básicos se encuentran bolean, char, integer y real. Un tipo básico especial, la función error_tipo(), señalará un error durante la com-probación de tipos.
• El
•
Verificación de tipos (Cont.)
Por ejemplo, la declaración en Pascal Var A: array[1..10] of integer; Asocia la expresión de tipo a rray(1..10, integer) con A.
b) Productos. Si T1 y T2 son expresiones de tipo, entonces su producto
cartesiano T 1 x T 2 es una expresión de tipo. Se supone que x es asociativa por la izquierda.
66
c) Registros. La diferencia entre un registro y un producto es que los
campos de un registro tienen nombres. El constructor de tipos record se aplicará a un conjunto ordenado formado con nombres de campos y tipos de campos. Por ejemplo, el fragmento de programa en Pascal: Type fila = record Dirección: integer; Lexema: array [1..15] of char end; var tabla: array [1..10] of fila;
11
19/09/2013
Record (dirección x integer ) x (lexema x array (1..15, char)) Y que la variable tabla es una matriz de registro de este tipo.
e) Funciones.- matemáticamente una función transforma los elementos de un conjunto, el dominio, a elementos de otro conjunto, el rango. Se pueden considerar las funciones dentro de los lenguajes de programación como transformaciones de un dominio de tipo D a un rango tipo R. La expresión tipo D R indica el tipo de dicha función.
d) Apuntadores.- Si T es una función de tipo, entonces pointer (T) es
• Por
Declara que el nombre de tipo fila representa la expresión de tipo.
67
ejemplo, la función predefinida mod de Pascal tiene un dominio de tipo int x int, es decir, un par de enteros, y rango de tipo int. De esta forma, se dice que mod tiene el tipo1.
una expresión de tipo que indica el tipo “apuntador a un objeto de T”. • Por ejemplo, en Pascal, la declaración
•
Var pila: ↑ fila Declara que la variable pila tiene tipo pointer (fila)
frecuencia existen, por razones de implantación, limitaciones que en cuanto al tipo que una función puede devolver; por ejemplo, matrices o funciones.
• Con
69
• Sin
int x int
• Una
(integer
• Con
(integer
el enfoque de la traducción dirigida por la sintaxis se puede construir un árbol o un GDA para una expresión de tipo, con nodos interiores para los constructores de tipos y hojas para los tipos básicos, nombres de tipos, y variables de tipos.
• Es
decir, g toma como argumento una función que transforma un entero en un entero y g produce como resultado otra función del mismo tipo.
Estáticamente puede que no se cumpla la condición: 0 <= i <= 80 al utilizar str [i]; es decir, esta comprobación debe efectuarse en forma dinámica. Sin embargo, es posible, por ejemplo, comprobar estáticamente si la asignación: str [i] := ch; está permitida, o sea, si ambos lados de la proposición de asignación son de tipos compatibles. Para ello es necesario consultar la tabla de símbolos.
70
manera conveniente de representar expresiones de tipo es utilizando un grafo.
integer)
Por ejemplo, consideremos la siguiente declaración: Str : array [0..80] of char; I : integer;
int
4.- Las expresiones de tipo pueden contener variables cuyos valores son expresiones de tipos.
embargo existen lenguajes que permiten que las funciones devuelvan objetos de tipos arbitrarios, así que se puede definir una función g de tipo integer);
68
• A
71
continuación se presenta un breve ejemplo de cómo introducir las reglas que permiten la comprobación de tipos de expresiones aritméticas.
72
• TipoE , TipoT
y TipoF , representan el tipo de una expresión, términos, etc.; se emplean subíndices para distinguir entre varias ocurrencias de un mismo símbolo no Terminal.
• Se
usará el siguiente subconjunto de la gramática G 0 con las reglas correspondientes:
12
19/09/2013
E E1
T T1
F F F
T E2 + T
F T2 * F
x y (E
(* TipoE := TipoT; *) (* TipoE1 := ifTipoE 2 = integer end TipoT = integer then integer else error_tipo; *) (* TipoT := TipoF *) (* TipoT1 := ifTipoT2 = integer end TipoF = integer then integer else error_tipo; *) (* TipoF := buscar_Tipo(x); *) (* TipoF := buscar_Tipo(y); *) (* TipoF := TipoE; *)
De esta forma, durante el proceso de análisis semántico, se puede determinar donde se requieren cambios forzados de tipo y qué operadores deben relacionarse (por ejemplo, multiplicación entera o de punto flotante). Se debe señalar que no todos los lenguajes de programación permiten una comprobación estática de tipos como la que se acaba de exponer, para ello se tiene que implementar en su diseño, para que pueda ser considerada.
Pila semántica en un ASem • La pila juega un papel f undamental en el desarrollo de cualquier analizador semántico. Dentro de cada elemento de la pila se guardan los valores que pueden tener una expresión.
73
• El
procedimiento buscar Tipo(...) se utiliza para determinar el tipo de un identificador por medio de la revisión de la tabla de símbolos.
74
• Por
ejemplo, el tipo de una expresión formada donde se aplica el operador “+” a una subexpresión y un término, es entero, si el tipo de la subexpresión y el término es entero; en caso contrario será un error de tipo.
•
La formulación de reglas para semánticas (tipo Pascal), que establecen, por ejemplo, que Entero * Real genera un resultado de tipo Real, es obvia y puede verse cómo se propagará un tipo.
75
1.4.- Pila Semántica en un Analizador Sintáctico
76
Admon. Tabla de Símbolos • La tabla de símbolos también recibe el nombre de ambiente. • Un ambiente contiene un conjunto de parámetros que sólo son visibles en ese ambiente. • • La tabla de símbolos se mantiene durante todo el proceso de traducción agregando elementos específicos en cada paso.
13
19/09/2013
Tabla de Símbolos Tabla de símbolos • Funcionalidades básicas:
• ¿Cómo se agrega la semántica al analizador sintáctico?
• Inserta(símbolo)
• Declaración TIPO {tipo=obtengo(yytext());} listavar PYC
• Existe(nombre) • • Se agrega una primitiva más: Tipo (nombre)
• listavar var {inserta(símbolo);} | var {inserta(simbolo);}
• ¿El análisis léxi co crea la tabla de símbolos?
• var ID {simbolo=yytext; símbolo.tipo=tipo; simbolo.amb=ambito;}
• No
Tabla de Símbolos
Exprlog PI exprlog{A=A;} PD | NOT exprlog {A=A;} | exprlog {A1=A;} OPLOG exprlog {
• int a; • a = (int) 10.1;
A2=A if(A1==INT && A2==INT) A=INT; else A=ERROR_TIPO; }
Tabla de Símbolos
Implementación de la tabla de símbolos 83
tabla de símbolos es una estructura de datos empleada para registrar las declaraciones de identificadores.
84
• Una
• Los identificadores • Con atributos • Y
• Nombres
se almacenan cuando se declaran
de clases, métodos, variables, etc
sub-atributos
• p ublic, private, integer, float, static, array, etc • Su localización en la pila si es variable local
• La
tabla de símbolos se consulta para cualquier uso.
• Verificación semántica y generación de código.
• La estructura es una pila • O una lista ligada que opera como pila • Operaciones • add_symbol(x) inserta x y la información asociada, tal como el tipo, en la pila. • find_symbol(x) busca en la pila, comenzando del tope de la pila.Regresa el primer x encontrado o NULL si no se encontro. • remove_symbol() saca elemento de la pila
14
19/09/2013
Una tabla de símbolos más elaborada • • • •
•
Definición de clase 85 • Los
nombres de clases se pueden usar antes de que sean definidos. • Usualmente no se puede verificar esto para los nombres de clases.
enter_scope() comenzar un nuevo ámbito anidado find_symbol(x) encuentra el x actual (o null) add_symbol(x) agrega un símbolo x a la tabla check_scope(x) verdadero si x esta definido en el ámbito actual (verificar declaraciones múltiples) exit_scope() salir del ámbito • Desc artar todos los símbolos del ámbito reciente
• Usando una tabla de símbolos (compilación separada?) • O en una pasada (a menos que se requieran prototipos)
Definición de clase • Solución usual • Fase 1: Junta todos los nombres de clases (+ otras cosas) • Fase 2: Realiza la verificación • El
86
87
Implementación de una Tabla de Símbolos simple
88
análisis semántico completo requiere varias pasadas.
• Probablemente
más de una
• La mayoría
de compiladores en JAVA busca por archivos de clases previamente compiladas.
Análisis semántico estático: se realiza en tiempo de compilation, no de execution. 1. Cómo vamos a especificar (describir) la estructura semántica de un lenguaje? Mediante gramáticas de atributos.
•
2. Cómo vamos a implementar la estructura semántica de un lenguaje? • A
partir de la construcción del árbol de análisis sintáctico, lo recorreremos en un determinado orden y calcularemos en cada nodo la información semántica necesaria (el valor de una expresión, el tipo de una variable, su ámbito de declaración, el número de argumentos de una función, etc).
89
1.5.- Esquemas de Traducción
90
Los programas de aplicación, los videojuegos y otras herramientas que se ejecutan en las computadoras, generalmente se realizan en lenguajes de alto nivel. Estos programas escritos en lenguajes de alto nivel necesitan ser traducidos a un lenguaje que sea entendible por la computadora, este lenguaje es el lenguaje de bajo nivel.
15
19/09/2013
1.3.- Comprobaciones de Tipos en Expresiones 1.4.- Pila Semántica en un Analizador Sintáctico
91
Sistemas de Tipos.
92
• El
diseño de un comprobador de tipos para un lenguaje se basa en información acerca de las construcciones sintácticas del lenguaje, la noción de tipos y las reglas para asignar tipos a las construcciones de lenguajes.
• Los
siguientes extractos de Pascal y del manual de referencia de C, respectivamente, son ejemplos de la información con la que el diseñador de un compilador podría verse obligado a comenzar.
• “Si ambos
operandos de los operadores aritméticos de suma, sustracción y multiplicación son de tipo entero, entonces el resultado es de tipo entero.”
93
• “El resultado
del operando unario & es un apuntador hacia el objeto al que se refiere el operando. Si el tipo del operando es ‘Un Arreglo’, o el tipo del resultado es ‘apuntador a’ ”. En los anteriores extractos se encuentra implícita la idea de que cada expresión tiene asociado un tipo.
En los lenguajes Pascal y C, los tipos de datos son básicos o construidos. Los tipos básicos son los tipos atómicos sin estructura interna por lo que concierne al programador. En Pascal, los tipos básicos son bolean, carácter, integer y real. Los tipos de subrango, como 1..10, y los tipos enumerados, como:
94
• (Violeta, azul, verde, amarillo, naranja, rojo)
Se pueden considerar como tipos básicos.
El lenguaje Pascal admite que un programador construya tipos a partir de tipos básicos y otros tipos construidos, como por ejemplo, las matrices o arreglos (array), los registros (record) y los conjuntos (set). Además, los apuntadores y las funciones también pueden considerarse como tipos construidos. Estos son algunos ejemplos de tipos de datos construidos que se pueden realizar con los lenguajes de alto nivel.
95
Todos los programas escritos en lenguajes de alto nivel deben ser traducidos a lenguaje de máquina mediante un traductor.
96
La función de un traductor de un lenguaje de programación, es traducir programas escritos en un lenguaje de alto nivel a programas equivalentes, expresados en lenguaje máquina. Después del proceso de traducción, los programas pueden ejecutarse en cualquier computadora a través de un intérprete microprogramado
16
19/09/2013
97
98
La siguiente figura muestra el funcionamiento de un traductor mediante un compilador de cuatro pasos con las demás actividades que intervienen durante la compilación de un programa hasta llegar a convertir el programa escrito en lenguaje de alto nivel a un programa escrito en lenguaje ensamblador o lenguaje binario, para que posteriormente se convierta en programa objeto:
Tipos de Traducción de lenguajes.
99
Interpretados.- Este tipo de traducción lee y ejecu-ta instrucción por instrucción. LIPS, Prolog, Basic y Smalltalk, se suelen implementar a través del uso de un interprete . En esta clase de lenguajes, el traductor no procede código de máquina para la computadora que sé esta utilizando. En su lugar, el traductor produce alguna forma inter-media del programa cuya ejecución es más fácil que la forma del programa original, pero que es distinta del código máquina.
Existe otro tipo de traducción que se realiza directa-mente por el procesador, esta traducción se describe a continuación: Ensamblador.-Es un traductor cuyo lenguaje objeto es también alguna variedad de lenguaje máquina para una computadora real pero cuyo lenguaje fuente, un lenguaje ensamblador constituye en gran medida una representación simbólica del código de máquina objeto. Casi todas las instrucciones en el lenguaje fuente se traducen una por una a cada instrucción del lenguaje objeto.
Compilados.- Los lenguajes por lo general como el C, Pascal, java, son lenguajes que se compilan. Esto significa que los programas en estos lenguajes se traducen ordinariamente al lenguaje máquina de la computadora real que sé esta usando antes que inicie la ejecución,
100
y la simulación está confinada a un con-junto de rutinas de apoyo en tiempos de ejecución que simulan operaciones primitivas en el lenguaje fuente para las cuales no existe un análogo cercano en el lenguaje máquina.
101
Cargador.- es un traductor cuyo lenguaje objeto es un código de máquina real y cuyo lenguaje fuente es casi idéntico; y está compuesto por lo general de programas en lenguaje máquina en forma reubicable junto con tablas de datos que especifican puntos donde el código reubicable se debe codificar para volverlo automáticamente ejecutable.
102
Preprocesador o Macroprocesador.- es un tra-ductor cuyo lenguaje fuente es una forma ampliada de un lenguaje de alto nivel cuyo lenguaje objeto es la forma estándar del mismo lenguaje.
17
19/09/2013
1.6.- Generación de la Tabla de Símbolos y de Direcciones.
104
Una tabla de símbolos es una estructura de datos que contiene una entrada para cada identificador encontrado en el programa fuente. Con el objeto de que el compilador pueda llevar un control de la información sobre el ámbito y el enlace de los nombres de datos que intervienen en el progra-ma fuente. 1.6.- Generación de la Tabla de Símbolos y de Direcciones.
Durante la compilación de un programa, cada vez que se encuentra un identificador en la ta-bla de símbolos, se puede realizar lo siguiente: •
La tabla puede ser consultada para ver si es necesario dar de alta el nuevo identificador.
•
En la tabla se puede completar los atributos faltantes de un identificador ya existente.
•
Se pueden recuperar los atributo de un iden-tificador ya existente en la tabla de símbolos.
103
105
Un compilador debe ser capaz de aumentar dinámicamente la tabla de símbolos durante la compilación.
• Si
la tabla de símbolos tiene tamaño fijo al escribir el compilador, entonces el tamaño debe de ser lo suficientemente grande como para almacenar cualquier programa fuente.
• Es
muy probable que dicho tamaño sea demasiado grande para la mayoría de los programa e inadecuado para algunos.
• Para
mantener uniformes los registros de la tabla de símbolos, es conveniente guardar una parte de la información de un nombre fuera de la entrada de la tabla, almacenando en el registro sólo un apuntador a esta infor-mación, para hacer referencia a él.
• Los
dos mecanismos para tablas de símbolos presentadas a continuación son listas lineales y tablas de dispersión.
106
Cada uno de estos mecanismos se evalúa basán-dose en el tiempo necesario para añadir n entra-das y realizar e consultas.
•
lista lineal es lo más fácil de implantar, pero su rendimiento es pobre cuando e y n se vuelven más grandes.
• Una
Un mecanismo de tabla de símbolos debe permitir añadir nuevas entradas y encontrar las entradas existentes de un analizador léxico eficientemente.
•
Los campos de cada entrada en la tabla de sím-bolos corresponden a los atributos de cada i-dentificador. (Tipo, valor, dirección, parámetros, etc.).
107
•
Las tablas de dispersión proporcionan un mayor rendimiento con esfuerzo algo mayor de programación y gasto de espacio.
•
Ambos mecanismos pueden adaptarse rápida-mente para funcionar las reglas del anidamiento más cercano.
Administración de la Tabla de Símbolos.
108
Cada entrada de la tabla de símbolos corresponde a la declaración de un nombre. El formato de las entradas no tiene que ser uniforme porque la información de un nombre depende del uso de dicho nombre. • Cada entrada se puede implantar como un registro que conste de una secuencia de palabras consecutivas de memoria. •
• No
toda la información se introduce en la tabla de símbolos.
• •
Las palabras clave se introducen, al inicio. El analizador léxico busca secuencias de letras y dígitos en la tabla de símbolos para determinar si se ha encontrado una palabra clave o un nombre.
18
19/09/2013
Las palabras clave deben estar en la tabla de símbolos antes de que comience el análisis léxico.
109
• Si
el analizador léxico reconoce las palabras clave, entonces no necesitan aparecer en la tabla de símbolos.
110
• Si
el lenguaje no convierte en palabras reservadas las palabras clave, entonces es indispensable que las palabras clave se introduzcan en la tabla de símbolos advirtiendo su posible uso como palabras clave.
• La
entrada misma de la tabla de símbolos puede establecerse cuando se aclara el papel de un nombre, y se llenan los valores de los atributos cuando se dispone de la información.
Fig. Una tabla de símbolos separada en dos partes, utilizando una parte como apuntador a dicha tabla para hacer referencia a los nombres almacenados en ella.
• En
algunos casos, el analizador léxico puede iniciar la entrada en cuanto aparezca un nombre en los datos de entrada.
• Se
crea el registro en la tabla de símbolos cuando se descubre el papel sintáctico que desempeña este nombre.
Un nombre puede indicar varios objetos distintos, quizás incluso en el mismo bloque o procedimiento. Por ejemplo, las declaraciones en C.
111
• Para
las declaraciones de la expresión, se crearían dos entradas en la tabla de símbolos para x; una con x como entero y otra como estructura.
int x; struct x { float y, z; } ; •
Utilizan x como entero y como etiqueta de una estructura con dos campos.
112
Los atributos de un nombre se introducen en respuesta a las declaraciones, que pueden ser implícitas.
• Las
etiquetas son identificadores seguidos de dos puntos, así que una acción asociada con el reconocimiento de dicho identifica-dor puede ser introducir este hecho en la tabla de símbolos.
En ambos casos, el analizador léxico sólo puede devolver al analizador sintáctico el nombre solo (o un apuntador al lexema que forma dicho nombre), en lugar de un apuntador a la entrada en la tabla de símbolos.
• La
sintaxis de las declaraciones de los procedimientos especifican que algunos identificadores son parámetros formales.
1.7.- Manejo de Errores Semánticos
114
Los errores que puede detectar el Analizador Sintáctico son aquellos que no cumplen las reglas de una gramática independiente del contexto.
1.7.- Manejo de Errores Semánticos
113
Una de las características de un lenguaje de programación es que no puede enunciarse con las reglas independientes del contexto, ya que dependen de él; por ejemplo, la restricción de que los identificadores deben declararse previamente antes de ser utilizados.
19
19/09/2013
Por tanto, los principales errores semánticos son los siguientes:
• No
115
a).-Identificadores no definidos; b).-Operadores y operandos incompatibles;
1.
116
• Sin
embargo, la mayoría de los errores semánticos pueden ser detectados mediante la revisión de la tabla de símbolos.
Es mucho más difícil introducir métodos formales para la recuperación de errores semánticos que para la recuperación de errores sintácticos, ya que con frecuencia la recuperación es ambigua.
117
FUENTES DE INFORMACION
obstante, puede requerirse que, por lo menos, el error semántico sea informado al programador, que se le ignore y que, por tanto, se suprimirá la generación de código.
FUENTES DE INFORMACION
118
Aho, Sethi, Ullman. Compiladores Principios, técnicas y herramientas. Ed. Addison Wesley.
2. Lemone Karen A. , Fundamentos de Compiladores Cómo traducir al lenguaje de computadora. Ed. Compañía Editorial Continental.
10. Beck. Software de Sistemas, Introducción a la programación de Sistemas. Ed. Addison-Wesley Iberoamericana.
3. Kenneth C. Louden. Construcción de compiladores Principios y práctica. Ed. Thomson.
11. Teufel, Schmidt, Teufel. Compiladores Conceptos Fundamentales. Ed. Addison-Wesley Iberoamericana.
4. Martin John, Lenguajes Formales y Teoría de la Computación, ED. Mc Graw Hill.
12. C. Louden, Kenneth. Lenguajes de programación Principios y práctica. Ed. Thomson.
5. Hopcroft John E., Introducción a la Teoría de Autómatas, Lenguajes y Computación, ED. Addison Wesley
13. Levine Gutiérrez, Guillermo. Computación y programación moderna Perspectiva integral de la informática. Ed. Pearson Educación.
6. Guerra Crespo. Hector. Compiladores. Ed. Tecnológica Didáctica. 7. Ronald Mark. Writing Compilers and Interpreters. Ed. Wiley Computer Publishing.
14. Abel, Peter. Lenguaje ensamblador y programación para PC IBM y compatibles. Ed. Pearson Educación.
8. Fischer, LeBlanc. Crafting a compiler with C. Ed. Cummings Publishing Company, Inc.
15. Mak, Ronald. Writing compilers and interpreters. Wiley Computer. Ed. Publishing.
9. Salas Parrilla, Jesús. Sistemas Operativos y Compiladores. Ed. McGraw Hill.
16. Pittman, Thomas, Peters, James. The art of compiler design Theory and práctice. Prentice Hall.
Dudas????
119
20