Los compiladores son programas de computadora que traducen un lenguaje a otro. Toman como entrada un programa escrito en un lenguaje fuente y producen un programa equivalente escrito en su lenguaje objetivo. Un compilador es un programa con un número de líneas de código que puede variar de 10,000 a 1,000,000. No obstante, los compiladores se utilizan en casi todas las formas de computación, y cualquiera que esté involucrado profesionalmente con las computadoras debería conocer la organización y el funcionamiento básicos de un compilador.
¿Por Qué Compiladores? Una Breve Historia
Con el advenimiento de la computación con programa almacenado, iniciado por John von Neumann a finales de la década de 1940, se hizo necesario escribir secuencias de códigos o programas que darían como resultado que estas computadoras realizaran los cálculos deseados. Al principio, estos programas se escribían en lenguaje de máquina.
Por ejemplo: C7 06 0000 0002
representa la instrucción para mover el número 2 a la ubicación 0000 en los procesadores 8×86.
La escritura de tales códigos es muy tediosa y consume mucho tiempo, por lo que esta forma fue reemplazada por el lenguaje ensamblador, en el cual las instrucciones y las localidades de memoria son formas simbólicas dadas. Un ensamblador traduce los códigos simbólicos y las localidades de memoria del lenguaje ensamblador a los códigos numéricos correspondientes del lenguaje de máquina.
El lenguaje ensamblador mejoró la velocidad y exactitud con la que podían escribirse los programas; sin embargo, tiene varios defectos: aún no es fácil de escribir y es difícil de leer y comprender. Además, depende en extremo de la máquina en particular para la cual se haya escrito, de manera que el código escrito para una computadora debe volver a escribirse para otra máquina.
El siguiente paso en la tecnología de programación fue escribir las operaciones de un programa de manera que fueran independientes de cualquier máquina en particular y todavía se pudieran traducir mediante un programa para convertirlas en código ejecutable.
Para ello se creó el lenguaje FORTRAN y su compilador por John Backus entre 1954 y 1957. El éxito de este proyecto se debió sólo a un gran esfuerzo, ya que la mayoría de los procesos involucrados en la traducción de los lenguajes de programación no fueron bien comprendidos en el momento.
Al mismo tiempo que el primer compilador se estaba desarrollando, Noam Chomsky comenzó a estudiar la estructura del lenguaje natural. Sus estudios condujeron a la clasificación de los lenguajes de acuerdo con la complejidad de sus gramáticas y la potencia de los algoritmos necesarios para reconocerlas.
La Jerarquía de Chomsky
La jerarquía de Chomsky se compone de 4 niveles de gramáticas: tipo 0, tipo 1, tipo 2 y tipo 3, cada una de las cuales es una especialización de su predecesora.
Las gramáticas de tipo 2 o gramáticas libres de contexto demostraron ser las más útiles para los lenguajes de programación. El estudio del problema del análisis sintáctico en la actualidad se ha vuelto una parte estándar de la teoría de compiladores.
Los Autómatas Finitos y las Expresiones Regulares
Los autómatas finitos y las expresiones regulares corresponden a las gramáticas tipo 3 de Chomsky, se encuentran estrechamente relacionados con las gramáticas libres de contexto. Al comenzar a generarse casi al mismo tiempo que el trabajo de Chomsky, su estudio condujo a métodos simbólicos para expresar la estructura de palabras, o tokens, de un lenguaje de programación. A medida que el problema del análisis sintáctico se comprendía, se dedicó mucho trabajo a desarrollar programas que automatizarían esta parte del desarrollo de compiladores, llamados generadores de analizadores sintácticos.
Otros Tipos de Traductores de Lenguaje
Intérpretes
Un intérprete es un traductor de lenguaje, igual que un compilador, pero difiere de este 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.
Ensambladores
Un ensamblador es un traductor para el lenguaje ensamblador de una computadora en particular.
Ligadores
Un ligador recompila el código que se compila o ensambla por separado en diferentes archivos objeto, a un archivo que es directamente ejecutable.
Cargadores
Con frecuencia, un cargador, ensamblador o ligador producirá un código que todavía no está organizado y listo para ejecutarse. Se dice que este código es relocalizable, y un cargador resolverá todas las direcciones relocalizables relativas a una dirección base, o de inicio, dada. El uso de un cargador hace más flexible el código ejecutable, pero el proceso de carga con frecuencia ocurre en segundo plano.
Preprocesadores
Un preprocesador es un programa separado que es invocado por el compilador antes de que comience la traducción real. Un preprocesador de este tipo puede eliminar los comentarios, incluir otros archivos y ejecutar sustituciones de macro (una macro es una descripción abreviada de una secuencia repetida de texto). Los preprocesadores pueden ser requeridos por el lenguaje (como en C) o pueden ser agregados posteriores que proporcionen facilidades adicionales.
Editores
Los compiladores por lo regular aceptan programas fuente escritos utilizando cualquier editor que pueda producir un archivo estándar, tal como un archivo ASCII. Más recientemente, los compiladores han sido integrados con editores y otros programas en un ambiente de desarrollo interactivo. Mientras que un editor produce archivos estándar, puede ser orientado hacia la estructura del lenguaje de programación en cuestión. A estos editores se les denomina basados en estructura y ya incluyen algunas de las operaciones de un compilador.
Depuradores
Un depurador es un programa que puede utilizarse para determinar los errores de ejecución de un programa compilado. También puede detener la ejecución en ubicaciones previamente especificadas, denominadas puntos de ruptura, además de proporcionar información de cuáles funciones se han invocado y cuáles son los valores actuales de las variables.
Perfiladores
Un perfilador es un programa que recolecta estadísticas sobre el comportamiento de un programa objeto durante la ejecución. Tales estadísticas pueden ser muy útiles para ayudar a mejorar la velocidad de ejecución del programa.
Administradores de Proyecto
Los modernos proyectos de software por lo general son tan grandes que tienen que ser emprendidos por grupos de programadores en lugar de un solo programador. En tales casos, es importante que los archivos que se están trabajando por personas distintas se encuentren coordinados, y este es el trabajo de un programa de administración de proyectos. Un administrador de proyecto puede escribirse en una forma independiente del lenguaje, pero cuando se integra junto con un compilador, puede mantener información acerca del compilador específico y las operaciones de ligado necesarias para construir un programa ejecutable completo.
Fases de un Compilador
Un compilador se compone de varias etapas, o fases, que realizan distintas operaciones lógicas.
En la siguiente figura se muestran las fases de un compilador junto con los 3 componentes auxiliares que interactúan con alguna de ellas o con todas.
Analizador Léxico o Rastreador
Esta fase del compilador efectúa la lectura real del programa fuente, el cual generalmente está en 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.
Ejemplo: A[INDEX] = 4 + 2
Este código contiene 12 caracteres diferentes, incluyendo el espacio en blanco, pero solo 8 tokens.