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?
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?
Respuestas:
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 características 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:
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 .
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 quine 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 esa última línea en
if (c == 'n') return '\n';
... y hará lo correcto! El 10
viene 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.
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:
Estoy seguro de que puede aceptar 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 de 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.
¿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 mirar:
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 .
Ahora necesita agregar nuevas funciones. Digamos que solo ha implementado while
-loops, pero también quiere for
-loops. Esto no es un problema, ya que puede reescribir cualquier for
bucle de tal manera que sea un while
bucle. 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 for
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 for
bucles! Ahora puede volver al archivo fuente de su compilador y reescribir cualquier while
bucle que no quiera en for
bucles.
Enjuague y repita hasta que se puedan compilar todas las características de idioma que desee con el compilador.
while
y for
obviamente 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.
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 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.
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 compilador de alojamiento propio .
Es extremadamente común, y generalmente resulta del deseo de un autor de usar su propio idioma para mantener el crecimiento de ese idioma.
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. Hay 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).
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).
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.
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 entre idiomas compilan una especificación de idioma en otra especificación de idioma.
Básicamente, la compilación es una traducción justa, y el nivel suele ser de un nivel de lenguaje superior al de lenguaje inferior, 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 adicionales de lenguaje complejo en adelante, siempre y cuando la nueva característica se pueda expresar usando las características anteriores. Si ese no fuera el caso, sería necesario que esa parte del "compilador" se desarrollara en otro idioma de antemano.
self-hosting
compilador. Ver programmers.stackexchange.com/q/263651/6221