tipos que programacion partes fases ejemplos ejecutar compilar compiladores compilador compilation

compilation - partes - que es un compilador en programacion



¿Cómo puede compilarse un compilador? (9)

¿Cómo puede compilarse un compilador, o qué significa esta declaración?

Significa exactamente eso. En primer lugar, algunas cosas a tener en cuenta. Hay cuatro objetos que debemos observar:

  • El código fuente de cualquier programa arbitrario de CoffeScript
  • El ensamblado (generado) de cualquier programa arbitrario de CoffeScript
  • El código fuente del compilador CoffeScript.
  • El ensamblado (generado) del compilador CoffeScript

Ahora, debería ser obvio que puede usar el ensamblado generado, el ejecutable, del compilador CoffeScript para compilar cualquier programa arbitrario de CoffeScript y generar el ensamblado para ese programa.

Ahora, el compilador de CoffeScript en sí mismo es solo un programa arbitrario de CoffeScript y, por lo tanto, puede ser compilado por el compilador de CoffeScript.

Parece que su confusión se debe al hecho de que cuando crea su propio idioma nuevo, aún no tiene un compilador que puede usar para compilarlo. Esto seguramente parece un problema de huevo de gallina , ¿verdad?

Introduce el proceso llamado bootstrapping .

  1. Usted escribe un compilador en un lenguaje ya existente (en el caso de CoffeScript, el compilador original se escribió en Ruby) que puede compilar un subconjunto del nuevo lenguaje
  2. Usted escribe un compilador que puede compilar un subconjunto del nuevo idioma en el nuevo idioma. Solo puede usar funciones de lenguaje que el compilador del paso anterior puede compilar.
  3. Utiliza el compilador del paso 1 para compilar el compilador del paso 2. Esto te deja con un ensamblado que se escribió originalmente en un subconjunto del nuevo idioma y que puede compilar un subconjunto del nuevo idioma.

Ahora necesita agregar nuevas funciones. Supongamos que solo ha implementado while -loops, pero que también quiere for -loops. Esto no es un problema, ya que puede volver a escribir cualquier for -loop de tal manera que sea un while -loop. Esto significa que solo puede usar while -loops en el código fuente de su compilador, ya que el ensamblado que tiene a mano solo puede compilarlos. Pero puede crear funciones dentro de su compilador que pueden pasar y compilar bucles con él. Luego usa el ensamblaje que ya tiene y compila la nueva versión del compilador. ¡Y ahora tiene un ensamblaje de un compilador que también puede analizar y compilar -loops! Ahora puede volver al archivo de origen de su compilador y reescribir los bucles while que no desee for bucles for .

Enjuague y repita hasta que se puedan compilar todas las características del idioma que desee con el compilador.

obviamente, while solo fueron ejemplos, pero esto funciona para cualquier nueva función de idioma que desee. Y entonces estás en la situación en la que se encuentra CoffeScript ahora: el compilador se compila solo.

Hay mucha literatura por ahí. Reflexiones sobre confiar en la confianza es un clásico que todos los interesados ​​en ese tema deben leer al menos una vez.

Estoy investigando CoffeeScript en el sitio web http://coffeescript.org/ , y tiene el texto

El compilador de CoffeeScript está escrito en CoffeeScript

¿Cómo puede compilarse un compilador, o qué significa esta declaración?


Prueba por inducción

Paso inductivo

La versión n + 1 del compilador está escrita en X.

Por lo tanto, puede ser compilado por la enésima versión del compilador (también escrita en X).

Caso base

Pero la primera versión del compilador escrita en X debe ser compilada por un compilador para X que esté escrito en un lenguaje que no sea X. Este paso se denomina bootstrapping del compilador.


Una pequeña pero importante aclaración.

Aquí el término compilador pasa por alto el hecho de que hay dos archivos involucrados. Uno es un ejecutable que toma como archivos de entrada escritos en CoffeScript y produce como archivo de salida otro ejecutable, un archivo de objeto enlazable o una biblioteca compartida. El otro es un archivo fuente de CoffeeScript que simplemente describe el procedimiento para compilar CoffeeScript.

Aplica el primer archivo al segundo, produciendo un tercero que es capaz de realizar el mismo acto de compilación que el primero (posiblemente más, si el segundo archivo define características no implementadas por el primero), por lo que puede reemplazar el primero si entonces deseo.


  1. El compilador de CoffeeScript se escribió por primera vez en Ruby.
  2. El compilador de CoffeeScript fue reescrito en CoffeeScript.

Como la versión Ruby del compilador CoffeeScript ya existía, se utilizó para crear la versión CoffeeScript del compilador CoffeeScript.

Esto se conoce como un bootstrapping .

Es extremadamente común, y generalmente resulta del deseo de un autor de usar su propio idioma para mantener el crecimiento de ese idioma.


En el artículo Reflections on Trusting Trust , Ken Thompson, uno de los creadores de Unix, escribe una descripción fascinante (y fácilmente legible) de cómo se compila el compilador de C. Se pueden aplicar conceptos similares a CoffeeScript o cualquier otro lenguaje.

La idea de un compilador que compila su propio código es vagamente similar a una quine : código fuente que, cuando se ejecuta, produce como salida el código fuente original. Aquí hay un ejemplo de una quine de CoffeeScript. Thompson dio este ejemplo de una quina C:

char s[] = { ''/t'', ''0'', ''/n'', ''}'', '';'', ''/n'', ''/n'', ''/'', ''*'', ''/n'', … 213 lines omitted … 0 }; /* * The string s is a representation of the body * of this program from ''0'' * to the end. */ main() { int i; printf("char/ts[] = {/n"); for(i = 0; s[i]; i++) printf("/t%d,/n", s[i]); printf("%s", s); }

A continuación, puede preguntarse cómo se le enseña al compilador que una secuencia de escape como ''/n'' representa el código ASCII 10. La respuesta es que en algún lugar del compilador C, hay una rutina que interpreta los literales de caracteres, que contiene algunas condiciones como esta para reconocer secuencias de barra invertida:

… c = next(); if (c != ''//') return c; /* A normal character */ c = next(); if (c == ''//') return ''//'; /* Two backslashes in the code means one backslash */ if (c == ''r'') return ''/r''; /* ''/r'' is a carriage return */ …

Entonces, podemos agregar una condición al código anterior ...

if (c == ''n'') return 10; /* ''/n'' is a newline */

... para producir un compilador que sepa que ''/n'' representa ASCII 10. Curiosamente, ese compilador, y todos los compiladores posteriores compilados por él , "conocen" esa asignación, por lo que en la próxima generación del código fuente, puede cambiar ese último línea en

if (c == ''n'') return ''/n'';

... y hará lo correcto! El 10 proviene del compilador, y ya no necesita ser definido explícitamente en el código fuente del compilador. 1

Ese es un ejemplo de una característica del lenguaje C que se implementó en el código C. Ahora, repita ese proceso para cada característica de lenguaje individual, y tiene un compilador de "autohospedaje": un compilador de C que está escrito en C.

1 El giro de la trama descrito en el documento es que, dado que al compilador se le pueden "enseñar" hechos como este, también se puede enseñar mal a generar ejecutables troyanados de una manera que sea difícil de detectar, y tal acto de sabotaje puede persistir en todos los compiladores producidos por el compilador contaminado.


La primera edición de un compilador no se puede generar a máquina a partir de un lenguaje de programación específico para él; Tu confusión es comprensible. El primer compilador podría construir una versión posterior del compilador con más funciones de lenguaje (con la fuente reescrita en la primera versión del nuevo idioma). Esa versión podría compilar el siguiente compilador, y así sucesivamente. Aquí hay un ejemplo:

  1. El primer compilador de CoffeeScript está escrito en Ruby, produciendo la versión 1 de CoffeeScript
  2. El código fuente del compilador CS se reescribe en CoffeeScript 1
  3. El compilador CS original compila el nuevo código (escrito en CS 1) en la versión 2 del compilador
  4. Se realizan cambios en el código fuente del compilador para agregar nuevas funciones de idioma
  5. El segundo compilador CS (el primero escrito en CS) compila el nuevo código fuente revisado en la versión 3 del compilador
  6. Repita los pasos 4 y 5 para cada iteración.

Nota: No estoy seguro exactamente cómo se numeran las versiones de CoffeeScript, eso fue solo un ejemplo.

Este proceso generalmente se llama bootstrapping . Otro ejemplo de un compilador de arranque es rustc , el compilador para el lenguaje Rust .


Los compiladores toman una especificación de alto nivel y la convierten en una implementación de bajo nivel, como la que se puede ejecutar en hardware. Por lo tanto, no existe una relación entre el formato de la especificación y la ejecución real, además de la semántica del lenguaje objetivo.

Los compiladores cruzados se mueven de un sistema a otro, los compiladores cruzados compilan una especificación de idioma en otra especificación de idioma.

Básicamente, compilar es una traducción justa, y el nivel suele ser de un nivel de lenguaje más alto a un nivel de lenguaje más bajo, pero hay muchas variantes.

Los compiladores de bootstrapping son los más confusos, por supuesto, porque compilan el lenguaje en el que están escritos. No olvide el paso inicial en bootstrapping que requiere al menos una versión mínima existente que sea ejecutable. Muchos compiladores de arranque trabajan primero en las características mínimas de un lenguaje de programación y agregan características de lenguaje complejo adicionales en el futuro siempre que la nueva característica se pueda expresar usando las características anteriores. Si ese no fuera el caso, requeriría que esa parte del "compilador" se desarrolle en otro idioma de antemano.


No se trata de compiladores aquí, sino de expresividad del lenguaje, ya que un compilador es solo un programa escrito en algún idioma.

Cuando decimos que "un idioma está escrito / implementado" en realidad queremos decir que se implementa un compilador o intérprete para ese idioma. Existen lenguajes de programación en los que puede escribir programas que implementan el lenguaje (son compiladores / intérpretes para el mismo lenguaje). Estos idiomas se llaman idiomas universales .

Para poder entender esto, piense en un torno de metal. Es una herramienta utilizada para dar forma al metal. Es posible, usando solo esa herramienta, crear otra herramienta idéntica, creando sus partes. Por lo tanto, esa herramienta es una máquina universal. Por supuesto, el primero se creó utilizando otros medios (otras herramientas) y probablemente fue de menor calidad. Pero el primero se usó para construir otros nuevos con mayor precisión.

Una impresora 3D es casi una máquina universal. Puede imprimir toda la impresora 3D con una impresora 3D (no puede construir la punta que derrite el plástico).


Ya ha recibido una muy buena respuesta, sin embargo, quiero ofrecerle una perspectiva diferente, que espero sea esclarecedora. Primero establezcamos dos hechos en los que ambos podemos estar de acuerdo:

  1. El compilador CoffeeScript es un programa que puede compilar programas escritos en CoffeeScript.
  2. El compilador CoffeeScript es un programa escrito en CoffeeScript.

Estoy seguro de que puedes estar de acuerdo en que tanto el número 1 como el número 2 son ciertos. Ahora, mira las dos declaraciones. ¿Ves ahora que es completamente normal que el compilador CoffeeScript pueda compilar el compilador CoffeeScript?

Al compilador no le importa lo que compila. Mientras sea un programa escrito en CoffeeScript, puede compilarlo. Y el compilador CoffeeScript en sí mismo es un programa de este tipo. Al compilador CoffeeScript no le importa que sea el compilador CoffeeScript lo que está compilando. Todo lo que ve es un código CoffeeScript. Período.

¿Cómo puede compilarse un compilador, o qué significa esta declaración?

Sí, eso es exactamente lo que significa esa declaración, y espero que puedan ver ahora cómo esa declaración es verdadera.