Traductores de Lenguaje

Anuncio
TRADUCTORES DE
LENGUAJE
Ing. Ma. Margarita Labastida Roldán
Verano 2013
Contenido






¿Porqué Compiladores?
Proceso de Traducción
Estructura de Datos
Arranque automático y portabilidad
Lenguaje y Compilador
C-Minus
Compiladores


Son programas de computadora que traducen un lenguaje a
otro.
Toma como entrada un programa escrito en lenguaje fuente y
produce un programa equivalente en lenguaje objetivo.
Programa
Fuente
Programa
Objetivo
Breve Historia
Jhon Von Neumann (1940)
Secuencia de códigos o programas, para cálculos. Escritos en principio en lenguaje máquina. [c7 06 0000 0002]
Lenguaje Ensamblador
Las instrucciones y las localidades de memoria son formas simbólicas
dadas. [MOV X,2].
Traduce los códigos simbólicos y las localidades de memoria del
lenguaje a código numéricos correspondientes del lenguaje máquina.
Lenguaje FORTRAN
Dirigido por John Backus entre 1954 y 1957, la mayoría de los procesos involucrados en la traducción de lenguajes de programación.
Noam Chomsky
Estructura del lenguaje natural. Permitió la semi-automatización de los
lenguajes.
Clasificación de los lenguajes de acuerdo con la complejidad de sus
gramáticas y la potencia de los algoritmos para reconocerlas
Breve Historia (2)
Jerarquía de Chomsky
Cuatro niveles, del tipo 0 al 3. Las del tipo 2 o gramáticas libres de contexto, son las más útiles para lenguajes de programación.
Autómatas Finitos y Expresiones Regulares
Corresponden a las gramáticas de tipo 3, relacionados con las
gramáticas libres de contexto.
El estudio condujo a métodos simbólicos para expresar la estructura
de palabras o tokens de un lenguaje de programación.
Técnicas de optimización
Técnicas de mejoramiento de código, producen un código objeto
óptimo y mejoran su eficacia.
Método para la generación de código objeto eficaz.
Generadores de analizadores sintácticos / Generadores de analizador léxico
Automatizan sólo una parte del proceso de compilación, el más
conocido es Yacc. Escrito por Steve Johnson en 1975
Cuyo representante más conocido es Lex, o generador de
analizadores léxicos.
Breve Historia (3)


Los compiladores han incluido la aplicación de
algoritmos más sofisticados para inferir y/o
simplificar la información contenido en un
programa.
Los compiladores se han vuelto una parte de un
ambiente de desarrollo interactivo, o IDE, basado
en ventanas, que incluyen editores, ligadores,
depuradores y administradores de proyectos.
Programas relacionados

Existen algunos programas
que están relacionados, o
que se utilizan, con los
compiladores y que con
frecuencia viene junto con
ellos en un ambiente de
desarrollo de lenguaje
completo.

Intérpretes

Ensambladores

Ligadores

Cargadores

Preprocesadores

Editores

Depuradores

Perfiladores
Intérpretes


Es un traductor de lenguaje, igual que un
compilador, pero difiere de éste en que ejecuta el
programa fuente inmediatamente, en vez de
generar un código objeto que se ejecuta después
de que se completa la traducción.
Cualquier lenguaje de programación se puede
interpretar o compilar, pero se puede preferir un
intérprete a un compilador dependiendo del
lenguaje que se use.
Intérpretes


Los lenguajes funcionales, como LISP tienden a ser
interpretados.
Los intérpretes también se utilizan en el desarrollo
de software, donde los programas son traducidos y
vueltos a traducir.
Ensambladores


Es un traductor para el lenguaje ensamblador de
una computadora en particular. El lenguaje
ensamblador es una forma simbólica del lenguaje
máquina de la computadora y es fácil de traducir.
Un compilador generará lenguaje ensamblador
como su lenguaje objetivo y dependerá de un
ensamblador para terminar la traducción a código
objeto.
Ligadores


Los compiladores y los ensambladores a menudo
dependen de un programa conocido como ligador, el
cual recopila el código que se compila o ensambla por
separado en diferentes archivos objeto, a un archivo
que es directamente ejecutable.
También conecta un programa objeto con el código de
funciones de librerías estándar, así como con recursos
suministrados por el sistema operativo de la
computadora.
Cargadores



Un compilador, ensamblador o ligador producirá un
código que no está completamente organizado y listo,
pero cuyas principales referencias de memoria se
hacen relativas a una localidad de arranque en una
memoria.
El código es relocalizable y un cargador resolverá
todas las direcciones relocalizables relativas a una
dirección base, o de inicio.
Hace flexible el código ejecutable, pero el proceso de
carga con frecuencia ocurre en segundo plano o
conjuntamente con el ligado.
Preprocesadores



Es un programa separado que es invocado por el
compilador antes de que comience la traducción
real.
Puede eliminar comentarios, incluir otros archivos y
ejecutar sustituciones de macro.
Pueden ser requeridos por el lenguaje o pueden ser
agregados
posteriores
que
proporcionen
facilidades adicionales.
Editores


Un editor, puede ser orientado hacia el formato o
estructura del lenguaje de programación.
Estos editores se denominan basados en estructura
y ya incluyen operaciones de un compilador, de
manera que pueda informarse al programador los
errores a medida que el programa se vaya
escribiendo en lugar de hacerlo cuando está
compilado.
Depuradores



Es un programa que puede utilizarse para determinar
los errores de ejecución en un programa compilado.
La ejecución de un programa con un depurador se
diferencia de la ejecución directa en que el depurador
se mantiene al tanto de la información sobre el código
fuente, como los números de línea y los nombres de las
variables y procedimientos.
Puede detener la ejecución en ubicaciones previamente
especificadas llamadas puntos de ruptura.
Depuradores


El compilador debe suministrar al depurador la
información simbólica apropiada.
La depuración se convierte en una cuestión de
compilación.
Perfiladores



Es un programa que recolecta estadísticas sobe el
comportamiento de un programa objeto durante la
ejecución.
Las estadísticas son el número de veces que se
llama a cada procedimiento y el porcentaje de
tiempo de ejecución que se ocupa en cada uno de
ellos.
A veces el compilador utilizará la salida del
perfilador para mejorar de manera automática el
código sin la intervención del programador.
Proceso de Traducción
Proceso de Traducción


Un compilador se compone internamente de varias
fases, que realizan distintas operaciones lógicas.
Las fases de un compilador se ilustran, junto con los
tres componentes auxiliares que interactúan con
alguna de ellas o con todas: la tabla de símbolos y
el manejador de errores.
Fases de un Compilador
Código Fuente
Analizador
léxico
Tokens
Analizador
sintáctico
Árbol Sintáctico
Analizador
semántico
Árbol con anotaciones
Optimizador de
código Fuente
Código Intermedio
Generador de
código
Código Objeto
Optimizador de
código objetivo
Código Objeto
Analizador léxico (Scanner)



Efectúa la lectura real del programa fuente, el cual
está en la forma de un flujo de caracteres.
Realiza lo que se conoce como análisis léxico:
recolecta secuencias de caracteres en unidades
significativas denominadas tokens, las cuales son
como las palabras de un lenguaje natural.
Realiza una función similar al deletreo.
Scanner
Ejemplo:
a[index] = 4 + 2

Este código contiene 12 caracteres diferentes de un
espacio en blanco pero sólo 8 tokens.
a
[
index
]
=
4
+
2
identificador
corchete izquierdo
identificador
corchete derecho
asignación
Número
signo más
número
Scanner


Cada token se compone de uno o más caracteres
que se reúnen en una unidad antes de algún
procesamiento adicional.
Un analizador léxico puede realizar otras funciones
junto con la de reconocimiento de tokens.
 Por
ejemplo: puede introducir identificadores en la
tabla de símbolos, y puede introducir literales en la
tabla de literales
Analizador sintáctico (Parser)

Recibe el código fuente en la forma de tokens
proveniente del analizador léxico y realiza el
análisis sintáctico, que determina la estructura del
programa.
 Es
semejante a realizar el análisis gramatical sobre una
frase en un lenguaje natural.

El análisis determina los elementos estructurales del
programa y sus relaciones. Los resultados se
representan como un árbol de análisis gramatical
o árbol sintáctico.
Parser
Parser



Los nodos internos del árbol de análisis están
etiquetados con los nombres de las estructuras que
representan y que la hojas del árbol representan la
secuencia de tokens de la entrada.
Un árbol de análisis gramatical es un auxiliar para
visualizar la sintaxis de un programa o de un elemento
de programa.
Los analizadores sintácticos tienden a generar un árbol
sintáctico que condensa la información contenida en el
árbol de análisis gramatical.
Parser (árbol sintáctico abstracto)
En el árbol sintáctico muchos de los nodos han
desaparecido (incluyendo los nodos de tokens). Si sabemos
que una expresión es una operación de subíndice, entonces
ya no será necesario mantener los paréntesis cuadrados
que representan esta operación en la entrada original.
Analizar Semántico




La semántica de un programa es su “significado”, en
oposición a su sintaxis, o estructura.
La semántica de un programa determina
comportamiento durante el tiempo de ejecución.
su
Existen algunas características como semántica
estática, y el análisis de esta semántica es la tarea del
analizador semántico.
Las características típicas de la semántica estática
incluyen las declaraciones y la verificación de tipos.
Analizar Semántico(2)

Los tipos de datos que se calculan mediante el
analizador semántico se llaman atributos y se
agregan al árbol como anotaciones o
“decoraciones”.
 En
el ejemplo: a[index] = 4 + 2 la información que se
tendría que obtener antes del análisis sería que a sea
un arreglo de valores enteros con subíndices
proveniente de un subintervalo de los enteros y que
index sea una variable entera.
Analizar Semántico(3)
 El
analizador semántico registrará el árbol sintáctico
con los tipos de todas las subexpresiones y
posteriormente verificará que la asignación tenga
sentido.
Optimizador de Código Fuente



Permite posibilidades de mejoramiento del código
que dependen del código fuente.
En el ejemplo, la expresión 4 +2 se puede calcular
previamente por el compilador(incorporación de
constantes).
Esta optimización se puede realizar de manera
directa sobre el árbol sintáctico al colapsar el
subárbol secundario de la derecha del nodo raíz.
Optimizador de Código Fuente(2)


Muchas optimizaciones se pueden efectuar directamente
sobre el árbol, pero en ocasiones es más fácil optimizar de
forma linealizada del árbol.
Existen variedades de código: código en tres
direcciones, código intermedio, representación intermedia
o RI.
Generador de Código



Toma el código intermedio y genera el código para
la máquina objetivo.
Se emplean instrucciones que existen en la máquina
objetivo, se considera la representación de los
datos , los bytes o palabras de memoria.
En el ejemplo se debe decidir cuántos enteros se
almacenarán para generar el código para la
indización del arreglo.
Generador de Código(2)

Una posible secuencia de código:
Optimizador de código objetivo


Intenta mejorar el código objetivo generado por el
generador de código.
Incluye la selección de modos de direccionamiento
para mejorar el rendimiento, reemplazando las
instrucciones lentas por rápidas y eliminando las
operaciones redundantes.
Optimizador de código objetivo(2)

En el ejemplo, se puede utilizar una instrucción de
desplazamiento para reemplazar la multiplicación
en la segunda instrucción. Se emplea un modo de
direccionamiento indizado para el almacenamiento.
Estructura de Datos de un
Compilador
Estructura de Datos Principales


Un compilador debería poder compilar un
programa en un tiempo proporcional al tamaño del
programa, es decir, en O(n) tiempo, donde n es una
medida del tamaño del programa (por lo general
el número de caracteres).
Existen algunas estructuras de datos que son
necesarias para las fases como parte de su
operación y que sirven para comunicar la
información entre fases.
Estructura de Datos Principales






Tokens
Árbol Sintáctico
Tabla de Símbolos
Tabla de Literales
Código Intermedio
Archivos Temporales
Tokens


Cuando un analizador léxico reúne los caracteres
en un token, generalmente representa el token de
manera simbólica, es decir, como un valor de un
tipo de datos enumerado que representa el
conjunto de tokens del lenguaje fuente.
En los lenguajes el analizador léxico sólo necesita
generar un token a la vez (búsqueda de símbolo
simple). Utilizando una variable global simple para
mantener la información del token
Árbol Sintáctico



Es una estructura estándar basada en un apuntador
que se asigna de manera dinámica a medida que
se efectúa el análisis sintáctico.
El árbol entero puede entonces conservarse como
una variable simple que apunta al nodo raíz.
Cada nodo en la estructura es un registro cuyos
campos representan la información recolectada
tanto por el analizador sintáctico como el
analizador semántico.
Estructura de Datos Principales


Cada nodo de árbol sintáctico por sí mismo puede
requerir de atributos diferentes para ser
almacenado, de acuerdo con la clase de estructura
del lenguaje que represente.
Cada nodo en el árbol sintáctico puede estar
representado por un registro variable, con cada
clase de nodo contenido solamente la información
necesaria para ese caso.
Tabla de símbolos



Esta estructura mantiene la información asociada con
los identificadores: funciones, variables, constantes y
tipos de datos.
La tabla de símbolos interactúa con casi todas las fases
del compilador: analizador léxico, analizador sintáctico
o semántico.
El primero introduce identificadores dentro de la tabla;
el semántico agregará tipos de datos y otra
información; las fases de optimización y generación de
código utilizan la tabla de símbolos para el código
objeto.
Tabla de símbolos



Ya que la tabla de símbolos tendrá solicitudes de
acceso con tanta frecuencia, las operaciones de
inserción, eliminación y acceso necesitan ser
eficientes.
Una estructura de datos estándar es la tabla de
dispersión o cálculo de dirección.
En ocasiones se utilizan varias tablas y se mantienen
en una lista o pila.
Tabla de Literales




Almacena constantes y cadenas utilizadas en el
programa.
Necesita impedir las eliminaciones porque sus datos se
aplican globalmente al programa y una constante o
cadena aparecerá sólo juna vez en esta tabla.
Es importante en la reducción del tamaño de un
programa en la memoria al permitir la reutilización de
constantes y cadenas.
Es importante para que el generador construya
direcciones simbólicas para las literales y para las
definiciones de datos en el archivo de código objeto.
Código Intermedio

De acuerdo con la clase de código intermedio y de
las clases de optimización realizadas, este código
puede conservarse como un arreglo de cadenas de
texto, un archivo de texto temporal o bien una lista
de estructuras ligadas.
Archivos Temporales


Eran usados para mantener los productos de los
pasos intermedios durante la traducción o bien al
compilar “al vuelo”, manteniendo sólo la
información suficiente de las partes anteriores del
programa fuente que permite proceder a la
traducción.
Los compiladores ocasionalmente encuentran útil
generar archivos intermedios durante alguna etapa
del procesamiento.
Archivos Temporales (2)

Algo común es la necesidad de direcciones de
corrección hacia atrás durante la generación del
código.
 Por
ejemplo, una sentencia condicional:
if x = 0 then … else …
Debe generarse un salto desde la prueba para la
parte bicondicional “else” antes de conocer la ubicación
del código para el “else”.
Archivos Temporales (3)
 Por
lo regular debe dejarse un espacio en blanco para
el valor de NEXT, el cal se llena una vez que se logra
conocer el valor.
 Lo anterior se consigue con el uso de un archivo
temporal.
Descargar