El Compilador - Análisis Léxico / Sintáctico / Semántico
Breve evolución de los Procesadores de Lenguajes
Evolución de los LP
Traductor
Programa que recibe como entrada código escrito en un cierto lenguaje y produce como salida código en otro lenguaje.
Hay dos tipos de traductores:
Ensamblador: traduce de lenguaje ensamblador a lenguaje máquina
Compilador: traduce de un lenguaje de alto nivel a un lenguaje de bajo nivel o a lenguaje máquina.
Interprete
Los interpretes en lugar de producir un programa objeto, realizan la operación que debería realizar el programa fuente.
Programa que toma una sentencia del programa fuente y la convierte a un código equivalente y al mismo tiempo lo ejecuta.
Diferencia entre Traductores e Interpretes
Uso de ambos procesadores
Sistema mixto: traductor/interprete.
Ejemplo: El JDK (Java Development Kit):
Traductor de Java a JVM-code (bytecodes Java)
Intérprete de JVM-code.
Compilador
Traductor que lee un programa fuente escrito en un lenguaje de alto nivel y lo traduce a un programa objeto escrito en lenguaje ensamblador o lenguaje maquina.
Realiza otras tareas:
Muestra mensajes de error.
Realiza determinadas “correcciones” (recuperación de errores).
Opcionalmente optimiza el código generado.
Los compiladores se pueden representar por Diagrama T:
L.F. (Lenguaje fuente): C, C++, Pascal, Java, Prolog, etc.
L.O. (Lenguaje objeto): lenguaje ensamblador o lenguaje maquina de un determinado microprocesador.
L.I. (Lenguaje de Implantación): lenguaje en que esta escrito el compilador; puede ser desde un lenguaje de alto nivel hasta un lenguaje maquina.
Contexto del Compilador
Editor: orientado al formato del lenguaje que llegan a incluir algunas de las operaciones propias del compiladores como informar de errores.
Depurador: para depurar un programa, se usa en un editor en general. Se ejecuta haciendo una traza del código ejecutado.
Preprocesador: antes de la compilación, elimina comentarios, incluye archivos de cabecera, sustituye macros y funciones en línea, etc.
Enlazador: junta los códigos compilados de distintos objetos en uno ejecutable. Incluye llamadas a bibliotecas y sistema ejecutable.
Cargador: modifica las direcciones relativas del código enlazado a direcciones absolutas.
Estructura del Compilador
Un compilador opera en fases, que se agrupan en dos etapas:
Etapa de análisis: divide el programa fuente en sus elementos componentes y crea una representación intermedia del programa fuente.
Etapa de síntesis: construye el programa objeto deseado a partir de la representación intermedia.
Cada fase transforma el programa fuente de una representación a otra.
front-end: fases dependientes del lenguaje fuente. back-end: fases dependientes del lenguaje objeto.
Análisis Léxico
El analizador léxico verifica que las palabras que conforman el programa fuente pertenezcan al léxico del lenguaje fuente.
El analizador léxico lee los caracteres que conforman el programa fuente hasta formar una palabra para luego ser clasificada bajo un tipo de token o tipo de palabra.
Los tipos de tokens comunes a la mayoría de los LP:
Palabras reservadas
Operadores aritméticos, relacionales, lógicos, especiales, etc.
identificadores
Constantes: numéricas, literales, lógicas, etc.
Ejemplo:
Análisis Sintáctico
El analizador sintáctico verifica que el programa fuente cumpla con la sintaxis que el lenguaje fuente tiene definido para sus programas.
El parser recibe los tokens que produce el scanner, para verificar que estén debidamente combinados de tal manera que cumplan con las reglas sintácticas del lenguaje fuente que se representan a través de una gramática.
El análisis de la cadena de tokens que se realiza mediante la gramática, y se representa mediante un árbol sintáctico el cual constituye la salida de la fase del análisis sintáctico.
Ejemplo:
Análisis Semántico
El analizador semántico verifica que se cumplan todas aquellas condiciones necesarias para el correcto significado de cada parte del programa fuente.
Recibe como entrada el árbol sintáctico que produce el analizador sintáctico, para verificar el cumplimiento de las reglas semánticas asociadas a cada parte del programa que se aprecia en dicho árbol sintáctico.
Realiza comprobaciones estáticas, tales como: verificación e inferencia de tipos en asignaciones y expresiones, declaración antes de uso, correcto uso de operadores, etc.
Ejemplo:
Generación de Código Intermedio
La generación de código intermedio transforma el árbol sintáctico (semántico) en una representación en un
lenguaje intermedio, que suele ser código suficientemente sencillo para poder luego generar código máquina.
Optimización de Código
Se realiza con el objetivo de que programa objeto:
Sea más rápido.
Necesite menos memoria para ejecutarse.
Optimizaciones dependen del lenguaje fuente:
eliminación de subexpresiones comunes
identificación de código muerto
sustitución de operaciones aritméticas
cálculo previo de constantes
variables de inducción
propagación de copias o código inalcanzable.
Es una fase opcional.
Generación de Código Objeto
El código intermedio optimizado es traducido a una secuencia de instrucciones en ensamblador o en el código de máquina relocalizable de la maquina destino.
Toma en consideración las siguientes características de la maquina destino:
Repertorio de instrucciones
Representación de los datos (número de bytes)
Modos de direccionamiento
Número y propósito de registros
Jerarquía de memoria (especialmente la caché)
Encauzamientos, etc.
Manejo de Errores
Cada fase puede encontrar errores y debe tratarlo de alguna forma para continuar la compilación, permitiendo detectar más errores en el programa fuente.
Se utiliza principalmente en las etapas de análisis sintáctico y semántico.
Es una tarea difícil, por dos motivos:
A veces unos errores ocultan otros.
A veces un error provoca una avalancha de muchos errores que se solucionan con el primero.
Manipulación de la Tabla de Símbolos (TS)
Estructura de datos que contiene información de cada identificador utilizado en el programa fuente.
La estructura de la TS varía de un lenguaje a otro; los datos que se almacenan depende de la categoría de símbolos que presenta el lenguaje fuente y los datos que se necesitan almacenar de estos.
De cada símbolo se almacena por lo general su nombre y un conjunto de atributos: categoría, tipo de dato, dimensión, alcance, dirección de memoria, etc.
La TS interactúa con las fases del proceso de compilación a través de las operaciones: insertar, consultar, eliminar y modificar.
Ejemplo de compilación
Especificación del Compilador
Herramientas para construcción de compiladores
Lex y YACC
Herramientas que nos permiten desarrollar componentes o la mayor parte de un compilador
Son un recurso invaluable para el profesional y el investigador
Existen paquetes freeware
__________________
Einstein dijo una vez: "Haz las cosas tan simples como sea posible pero no demasiado sencillas"