Bootstrapping aún requiere apoyo externo

96

He oído hablar de la idea de iniciar un lenguaje, es decir, escribir un compilador / intérprete para el lenguaje en sí. Me preguntaba cómo se podría lograr esto y miré un poco a mi alrededor, y vi a alguien decir que solo podía hacerlo cualquiera

  • escribir un compilador inicial en un idioma diferente.
  • codificar manualmente un compilador inicial en Ensamblador, que parece un caso especial de la primera

Para mí, ninguno de estos parece estar realmente iniciando un lenguaje en el sentido de que ambos requieren apoyo externo. ¿Existe alguna forma de escribir un compilador en su propio idioma?

pbh101
fuente
No tengo mucha experiencia con este tipo de cosas, pero supongo que el compilador inicial tendría que estar escrito en otro idioma. Estoy bastante seguro de que "bootstrapping", en referencia a los compiladores, simplemente se refiere a escribir un compilador para un lenguaje en el lenguaje que está destinado a compilar, no a escribir el primer compilador para el lenguaje en el lenguaje que debe compilar.
jdd
1
Gracias por la información a todos. Cuando se explica con la idea de escribir inicialmente un compilador limitado y luego construir sobre eso, entonces la idea de bootstrapping tiene más sentido. Estoy tomando una clase de Compiladores este semestre, una decisión influenciada en gran medida por la publicación de Steve Yegge sobre la importancia de una clase en Compiladores , y acabo de comprar una copia del libro Dragon del enlace de Amazon que se modificó tanto en SO antes.
pbh101
1
Consulte también una pregunta similar: Implementación de un compilador en sí mismo
Urban Vagabond

Respuestas:

107

¿Existe alguna forma de escribir un compilador en su propio idioma?

Debe tener algún lenguaje existente para escribir su nuevo compilador. Si estuviera escribiendo un compilador nuevo, digamos, C ++, simplemente lo escribiría en C ++ y lo compilaría primero con un compilador existente. Por otro lado, si estuviera creando un compilador para un nuevo lenguaje, llamémoslo Yazzleof, primero tendría que escribir el nuevo compilador en otro lenguaje. Generalmente, este sería otro lenguaje de programación, pero no tiene por qué serlo. Puede ser ensamblado o, si es necesario, código máquina.

Si fuera a iniciar un compilador para Yazzleof, generalmente no escribiría un compilador para el lenguaje completo inicialmente. En su lugar, escribiría un compilador para Yazzle-lite, el subconjunto más pequeño posible de Yazzleof (bueno, al menos un subconjunto bastante pequeño ). Luego, en Yazzle-lite, escribiría un compilador para el lenguaje completo. (Obviamente, esto puede ocurrir de forma iterativa en lugar de en un salto.) Debido a que Yazzle-lite es un subconjunto adecuado de Yazzleof, ahora tiene un compilador que puede compilarse a sí mismo.

Hay un artículo realmente bueno sobre cómo arrancar un compilador desde el nivel más bajo posible (que en una máquina moderna es básicamente un editor hexadecimal), titulado Bootstrapping a simple compiler from nothing . Se puede encontrar en https://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html .

Derek Park
fuente
19

La explicación que ha leído es correcta. Hay una discusión sobre esto en Compiladores: Principios, Técnicas y Herramientas (el Libro del Dragón):

  • Escriba un compilador C1 para el lenguaje X en el lenguaje Y
  • Utilice el compilador C1 para escribir el compilador C2 para el lenguaje X en el lenguaje X
  • Ahora C2 es un entorno de alojamiento totalmente autónomo.
Mark Harrison
fuente
7

Un súper interesante discusión de este está en Unix co-creador Ken Thompson 's Premio Turing conferencia.

Comienza con:

Lo que voy a describir es uno de los muchos problemas del "huevo y la gallina" que surgen cuando los compiladores se escriben en su propio idioma. En esta facilidad, usaré un ejemplo específico del compilador de C.

y procede a mostrar cómo escribió una versión del compilador Unix C que siempre le permitiría iniciar sesión sin contraseña, porque el compilador C reconocería el programa de inicio de sesión y agregaría un código especial.

El segundo patrón está dirigido al compilador de C. El código de reemplazo es un programa de reproducción automática Stage I que inserta ambos caballos de Troya en el compilador. Esto requiere una fase de aprendizaje como en el ejemplo de la Etapa II. Primero compilamos la fuente modificada con el compilador C normal para producir un binario con errores. Instalamos este binario como el oficial C. Ahora podemos eliminar los errores de la fuente del compilador y el nuevo binario reinsertará los errores cada vez que se compile. Por supuesto, el comando de inicio de sesión permanecerá con errores sin rastro en la fuente en ninguna parte.

Mark Harrison
fuente
9
Esto está fuera de tema ... Interesante, pero confuso, y no es una respuesta a la pregunta.
blueshift
5

La forma en que he oído hablar es escribir un compilador extremadamente limitado en otro idioma y luego usarlo para compilar una versión más complicada, escrita en el nuevo idioma. Esta segunda versión se puede utilizar para compilarse a sí mismo y la siguiente versión. Cada vez que se compila se utiliza la última versión.

Esta es la definición de bootstrapping:

el proceso de un sistema simple que activa un sistema más complicado que tiene el mismo propósito.

EDITAR: El artículo de Wikipedia sobre el arranque del compilador cubre el concepto mejor que yo.

Eric Haskins
fuente
4

Donald E. Knuth realmente construyó WEB escribiendo el compilador en él y luego lo compiló manualmente en código ensamblador o máquina.

MauganRa
fuente
3

Según tengo entendido, el primer intérprete Lisp se arrancó compilando manualmente las funciones del constructor y el lector de tokens. El resto del intérprete se leyó luego de la fuente.

Se puede comprobar por sí mismo mediante la lectura del documento McCarthy original, funciones recursivas de expresiones simbólicas y su cómputo por máquina, Parte I .

luser droog
fuente
¿Qué pasó con las partes 2 y 3? ... ¿Cómo no me di cuenta de que @Wing publicó lo mismo 3 años antes que yo? Soy un tonto. Al menos vinculé el papel (con ayuda).
luser droog
2

Otra alternativa es crear una máquina de código de bytes para su idioma (o usar una existente si sus características no son muy inusuales) y escribir un compilador para el código de bytes, ya sea en el código de bytes o en su idioma deseado usando otro intermedio, como un kit de herramientas del analizador que genera el AST como XML, luego compila el XML en código de bytes usando XSLT (u otro lenguaje de coincidencia de patrones y representación basada en árbol). No elimina la dependencia de otro idioma, pero podría significar que una mayor parte del trabajo de arranque termina en el sistema final.

Pete Kirkham
fuente
2

Es la versión informática de la paradoja del huevo y la gallina. No puedo pensar en una forma de no escribir el compilador inicial en ensamblador o en algún otro lenguaje. Si se hubiera podido hacer, debería haberlo hecho Lisp.

De hecho, creo que Lisp casi califica. Consulte su entrada de Wikipedia . Según el artículo, la función de evaluación Lisp podría implementarse en un IBM 704 en código de máquina, con un compilador completo (escrito en Lisp mismo) que entraría en vigor en 1962 en el MIT .

Ala
fuente
2

Cada ejemplo de arranque de un lenguaje en el que puedo pensar ( C , PyPy ) se realizó después de que hubiera un compilador en funcionamiento. Tienes que empezar en alguna parte, y volver a implementar un lenguaje en sí mismo requiere escribir un compilador en otro lenguaje primero.

¿De qué otra manera funcionaría? No creo que sea conceptualmente posible hacer otra cosa.

Adam Lassek
fuente
4
El primer compilador Lisp, al menos, se arrancó utilizando un intérprete Lisp existente . Así que no semánticamente en otro idioma, sino en otra implementación de idioma.
Ken
0

Algunos compiladores o sistemas bootstrapped mantienen tanto el formato fuente como el formato objeto en su repositorio:

  • ocaml es un lenguaje que tiene un intérprete de código de bytes (es decir, un compilador de código de bytes Ocaml) y un compilador nativo (para x86-64 o ARM, etc ... ensamblador). Su repositorio svn contiene tanto el código fuente (archivos */*.{ml,mli}) como el formato bytecode (archivo boot/ocamlc) del compilador. Entonces, cuando lo compila, primero usa su código de bytes (de una versión anterior del compilador) para compilarse a sí mismo. Más tarde, el código de bytes recién compilado puede compilar el compilador nativo. Entonces, el repositorio svn de Ocaml contiene tanto los *.ml[i]archivos fuente como el boot/ocamlcarchivo de código de bytes.

  • El compilador de rust descarga (usando wget, por lo que necesita una conexión a Internet que funcione) una versión anterior de su binario para compilarse.

  • MELT es un lenguaje similar a Lisp para personalizar y ampliar GCC . Se traduce a código C ++ mediante un traductor bootstrap. El código C ++ generado del traductor se distribuye, por lo que el repositorio svn contiene tanto *.meltarchivos fuente como archivos melt/generated/*.cc"objeto" del traductor.

  • El sistema de inteligencia artificial CAIA de J.Pitrat es completamente autogenerador. Está disponible como una colección de miles de [A-Z]*.carchivos generados (también con un dx.harchivo de encabezado generado ) con una colección de miles de _[0-9]*archivos de datos.

  • También se arrancan varios compiladores de Scheme. Scheme48, Plan de pollo, ...

Basile Starynkevitch
fuente