1.5. Tipos de Compiladores

El escritor del compilador, como cualquier programador, puede usar con provecho herramientas de software tales como depuradores, administradores de versiones, analizadores, etcétera. Además de estas herramientas de desarrollo de software, se han creado herramientas más especializadas para ayudar a implantar varias fases de un compilador. Poco después de escribirse el primer compilador, aparecieron sistemas para ayudar en el proceso de escritura de compiladores. A menudo se hace referencia a estos sistemas como compiladores de compiladoresgeneradores de compiladores o sistemas generadores de traductores. En gran parte, se orientan en torno a un modelo particular de lenguaje, y son más adecuados para generar compiladores de lenguajes similares al del modelo.

Por ejemplo, es tentador suponer que los analizadores léxicos para todos los lenguajes son en esencia iguales, excepto por las palabras clave y signos particulares que se reconocen. Muchos compiladores de compiladores de hecho producen rutinas fijas de análisis léxico para usar en el compilador generado. Estas rutinas sólo difieren en la lista de palabras clave que reconocen, y esta lista es todo lo que debe proporcionar el usuario. El planteamiento es válido, pero puede no ser funcional si se requiere que reconozca componentes léxicos no estándar, como identificadores que pueden incluir ciertos caracteres distintos de letras y dígitos.

Se han creado algunas herramientas generales para el diseño automático de componentes específicos de compilador. Estas herramientas utilizan lenguajes especializados para especificar e implantar el componente, y pueden utilizar algoritmos bastante complejos. Las herramientas más efectivas son las que ocultan los detalles del algoritmo de generación y producen componentes que se pueden integrar con facilidad al resto del compilador. La siguiente es una lista de algunas herramientas útiles para la construcción de compiladores:

I. Generadores de analizadores sintácticos. Estos generadores producen analizadores sintácticos, normalmente a partir de una entrada fundamentada en una gramática independiente del contexto. En los primeros compiladores, el análisis sintáctico consumía no sólo gran parte del tiempo de ejecución del compilador, sino gran parte del esfuerzo intelectual de escribirlo. Esta fase se considera ahora una de las más fáciles de aplicar. Muchos de los “pequeños lenguajes” utilizados en la composición de este libro, como, PIC (Kernighan ([1982]) y EQN, se aplicaron en unos días por medio del generador de analizadores sintácticos ya vistos. Muchos de los generadores de analizadores sintácticos utilizan poderosos algoritmos de análisis sintáctico, y son demasiado complejos para realizarlos manualmente.

2. Generadores de analizadores léxicos. Estas herramientas generan automáticamente analizadores léxicos. La organización básica del analizador léxico resultante es en realidad un autómata finito.

3. Dispositivos de traducción dirigida por la sintaxis. Estos producen grupos de rutinas que recorren el árbol de análisis sintáctico, como el de la figura 1.4, generando código intermedio. La idea básica es que se asocian una o más “traducciones” con cada nodo del árbol de análisis sintáctico, y cada traducción se define partiendo de traducciones en sus nodos vecinos en el árbol.

4. Generadores automáticos de código. Tales herramientas toman un conjunto de reglas que definen la traducción de cada operación del lenguaje intermedio al lenguaje de máquina para la máquina objeto. Las reglas deben incluir suficiente detalle para poder manejar los distintos métodos de acceso posibles a los datos: por ejemplo, las variables pueden estar en registros, en una posición fija (estática) de memoria o pueden tener asignada una posición en una pila. La técnica fundamental es la de “concordancia de plantillas”. Las proposiciones de código intermedio se reemplazan por “plantillas” que representan secuencias de instrucciones de máquina, de modo que las suposiciones sobre el almacenamiento de las variables concuerden de plantilla a plantilla. Como suele haber muchas opciones en relación con la ubicación de las variables (por ejemplo, en uno o varios registros o en memoria), hay muchas formas posibles de ‘cubrir’ el código intermedio con un conjunto dado de plantillas, y es necesario seleccionar una buena cobertura sin una explosión combinatoria en el tiempo de ejecución del compilador.

5. Dispositivos para análisis de flujo de datos. Mucha de la información necesaria para hacer una buena optimación de código implica hacer un ‘análisis de flujo de datos”, que consiste en b recolección de información sobre la forma en que se transmiten los valores de una parte de un programa a cada una de las otras partes. Las distintas tareas de esa naturaleza se pueden efectuar esencialmente con la misma rutina, en la que el usuario proporciona los detalles relativos a la relación que hay entre las proposiciones en código intermedio y la información que se está recolectando.

 

Clasificación de los compiladores

 

a) Compilador cruzado. Genera un código ejecutable en un ordenador distinto de aquel en que se realiza la compilación.

 

b) Compilador de montaje y ejecución. Se fragmenta el programa fuente en módulos que se compilan por separado, y una vez compilados se unen mediante un enlazador para formar un módulo ejecutable.

 

c) Compilador en una pasada. Examina el código fuente una sola vez, generando el código objeto.

 

d) Compilador de pasadas múltiples Requiere varias lecturas del programa fuente para producir y optimizar el código objeto.

 

e) Compilador de optimización. Lee el código fuente, lo analiza, optimiza y descubre errores potenciales sin ejecutar el programa.

 

f) Compilador incremental. Compila el programa fuente, en caso de detectar errores al volver a compilar el programa corregido, solo compila las modificaciones que se han hecho respecto al primero.

 

g) Ensamblador. El lenguaje fuente es el lenguaje ensamblador.

 

h) Autocompilador. Es el compilador que está escrito en el mismo lenguaje a compilar, básicamente nos sirve para hacer ampliaciones al lenguaje, mejorar el código generado, etc.

 

i) Metacompilador. “Compilador de compiladores”. Obtiene como entrada la definición de un lenguaje y como salida el compilador para dicho lenguaje.