Le bootstrapping nécessite toujours un support extérieur


96

J'ai entendu parler de l'idée d'amorcer un langage, c'est-à-dire d'écrire un compilateur / interpréteur pour le langage en lui-même. Je me demandais comment cela pouvait être accompli et j'ai regardé un peu autour de moi, et j'ai vu quelqu'un dire que cela ne pouvait être fait que par l'un ou l'autre

  • écrire un compilateur initial dans une langue différente.
  • coder à la main un compilateur initial dans Assembly, ce qui semble être un cas particulier du premier

Pour moi, ni l'un ni l'autre ne semblent réellement bootstrap sur un langage dans le sens où ils ont tous deux besoin d'un soutien extérieur. Existe-t-il un moyen d'écrire un compilateur dans son propre langage?


Je ne suis pas très expérimenté avec de telles choses, mais je suppose que le compilateur initial devrait être écrit dans une autre langue. Je suis assez certain que "bootstrapping", en référence aux compilateurs, se réfère simplement à l'écriture d' un compilateur pour un langage dans le langage qu'il est censé compiler, et non à l'écriture du premier compilateur pour le langage dans le langage qu'il est censé compiler.
jdd

1
Merci à tous pour l'information. Lorsqu'elle est expliquée avec l'idée d'écrire initialement un compilateur limité, puis de construire en plus de cela, alors l'idée de bootstrapping a plus de sens. Je prends un cours sur les compilateurs ce semestre, une décision largement influencée par l'article de Steve Yegge sur l'importance d'une classe dans Compilers , et je viens d'acheter une copie du livre Dragon sur le lien Amazon qui a été tellement remodelé sur SO plus tôt.
pbh101

1
Voir aussi question similaire: Implémentation d'un compilateur en lui
Urban Vagabond

Réponses:


107

Existe-t-il un moyen d'écrire un compilateur dans son propre langage?

Vous devez avoir un langage existant pour écrire votre nouveau compilateur. Si vous écriviez un nouveau compilateur, disons, C ++, vous l'écririez simplement en C ++ et le compileriez d'abord avec un compilateur existant. D'un autre côté, si vous créez un compilateur pour un nouveau langage, appelons-le Yazzleof, vous devrez d'abord écrire le nouveau compilateur dans un autre langage. En général, ce serait un autre langage de programmation, mais ce n'est pas obligatoire. Il peut s'agir d'un assemblage ou, si nécessaire, d'un code machine.

Si vous étiez allez démarrer un compilateur pour Yazzleof, vous généralement pas écrire un compilateur pour le langage complet au départ. Au lieu de cela, vous écririez un compilateur pour Yazzle-lite, le plus petit sous-ensemble possible du Yazzleof (enfin, un assez petit sous-ensemble au moins). Ensuite, dans Yazzle-lite, vous écririez un compilateur pour le langage complet. (Évidemment, cela peut se produire de manière itérative au lieu d'un saut.) Puisque Yazzle-lite est un sous-ensemble approprié de Yazzleof, vous avez maintenant un compilateur qui peut se compiler.

Il existe un très bon article sur l'amorçage d'un compilateur à partir du niveau le plus bas possible (qui sur une machine moderne est essentiellement un éditeur hexadécimal), intitulé Bootstrapping un simple compilateur à partir de rien . Il peut être trouvé à https://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html .


19

L'explication que vous avez lue est correcte. Il y a une discussion à ce sujet dans Compilers: Principles, Techniques, and Tools (the Dragon Book):

  • Ecrire un compilateur C1 pour le langage X en langage Y
  • Utilisez le compilateur C1 pour écrire le compilateur C2 pour le langage X en langage X
  • Maintenant, C2 est un environnement entièrement auto-hébergé.

7

Une super intéressante discussion de c'est sous Unix co-créateur Ken Thompson de Turing Award conférence.

Il commence par:

Ce que je suis sur le point de décrire est l'un des nombreux problèmes de «poule et œuf» qui surviennent lorsque les compilateurs sont écrits dans leur propre langue. Dans cette facilité, j'utiliserai un exemple spécifique du compilateur C.

et continue à montrer comment il a écrit une version du compilateur Unix C qui lui permettrait toujours de se connecter sans mot de passe, car le compilateur C reconnaîtrait le programme de connexion et ajouterait un code spécial.

Le deuxième modèle est destiné au compilateur C. Le code de remplacement est un programme d'auto-reproduction de l'étape I qui insère les deux chevaux de Troie dans le compilateur. Cela nécessite une phase d'apprentissage comme dans l'exemple de l'étape II. Nous compilons d'abord la source modifiée avec le compilateur C normal pour produire un binaire avec bogue. Nous installons ce binaire en tant que C. officiel. Nous pouvons maintenant supprimer les bogues de la source du compilateur et le nouveau binaire réinsérera les bogues chaque fois qu'il sera compilé. Bien sûr, la commande de connexion restera buggée sans aucune trace dans la source.


9
C'est hors sujet. Intéressant, mais déroutant, et pas une réponse à la question.
blueshift

5

La façon dont j'ai entendu parler est d'écrire un compilateur extrêmement limité dans un autre langage, puis de l'utiliser pour compiler une version plus compliquée, écrite dans le nouveau langage. Cette seconde version peut ensuite être utilisée pour se compiler, et la version suivante. Chaque fois qu'il est compilé, la dernière version est utilisée.

C'est la définition du bootstrapping:

le processus d'un système simple activant un système plus compliqué qui sert le même objectif.

EDIT: L' article de Wikipedia sur l'amorçage du compilateur couvre le concept mieux que moi.




3

Si je comprends bien, le premier interpréteur Lisp a été amorcé en compilant manuellement les fonctions du constructeur et le lecteur de jetons. Le reste de l'interprète a ensuite été lu à partir de la source.

Vous pouvez vérifier vous - même en lisant le papier McCarthy original, récursives Fonctions des expressions symboliques et leur calcul par la machine, la partie I .


Qu'est-il arrivé aux parties 2 et 3? ... Comment n'ai-je pas remarqué que @Wing avait posté la même chose 3 ans avant moi? Je suis un cancre. Au moins j'ai lié le papier (avec de l'aide).
luser droog

2

Une autre alternative est de créer une machine bytecode pour votre langage (ou d'utiliser une machine existante si ses fonctionnalités ne sont pas très inhabituelles) et d'écrire un compilateur en bytecode, soit dans le bytecode, soit dans la langue de votre choix en utilisant un autre intermédiaire - tel qu'un boîte à outils d'analyseur qui génère l'AST au format XML, puis compile le XML en bytecode en utilisant XSLT (ou un autre langage de correspondance de modèle et une représentation arborescente). Cela ne supprime pas la dépendance à une autre langue, mais pourrait signifier qu'une plus grande partie du travail d'amorçage se termine dans le système final.


2

C'est la version informatique du paradoxe de la poule et de l'œuf. Je ne peux pas penser à un moyen de ne pas écrire le compilateur initial en assembleur ou dans un autre langage. Si cela avait pu être fait, j'aurais dû que Lisp l'aurait fait.

En fait, je pense que Lisp est presque qualifié. Découvrez son entrée Wikipédia . Selon l'article, la fonction Lisp eval pourrait être implémentée sur un IBM 704 en code machine, avec un compilateur complet (écrit en Lisp lui-même) qui verrait le jour en 1962 au MIT .


2

Chaque exemple d'amorçage d'un langage auquel je peux penser ( C , PyPy ) a été réalisé après qu'un compilateur fonctionne. Il faut commencer quelque part, et réimplémenter un langage en lui-même nécessite d'abord d'écrire un compilateur dans un autre langage.

Sinon, comment cela fonctionnerait-il? Je ne pense pas qu'il soit même conceptuellement possible de faire autrement.


4
Le premier compilateur Lisp, au moins, a été amorcé en utilisant un interpréteur Lisp existant . Donc pas un autre langage sémantiquement, mais une autre implémentation de langage.
Ken

0

Certains compilateurs ou systèmes bootstrap gardent à la fois le formulaire source et le formulaire objet dans leur référentiel:

  • ocaml est un langage qui possède à la fois un interpréteur de bytecode (ie un compilateur vers le bytecode Ocaml) et un compilateur natif (vers x86-64 ou ARM, etc ... assembleur). Son référentiel svn contient à la fois le code source (fichiers */*.{ml,mli}) et la forme bytecode (fichier boot/ocamlc) du compilateur. Ainsi, lorsque vous le construisez, il utilise d'abord son bytecode (d'une version précédente du compilateur) pour se compiler. Plus tard, le bytecode fraîchement compilé est capable de compiler le compilateur natif. Ainsi, le référentiel Ocaml svn contient à la fois les *.ml[i]fichiers source et le boot/ocamlcfichier bytecode.

  • Le compilateur rust télécharge (en utilisant wget, vous avez donc besoin d'une connexion Internet fonctionnelle) une version précédente de son binaire pour se compiler.

  • MELT est un langage de type Lisp pour personnaliser et étendre GCC . Il est traduit en code C ++ par un traducteur amorcé. Le code C ++ généré du traducteur est distribué, de sorte que le référentiel svn contient à la fois les *.meltfichiers source et les fichiers melt/generated/*.cc"objet" du traducteur.

  • Le système d'intelligence artificielle CAIA de J.Pitrat est entièrement auto-générateur. Il est disponible sous la forme d'une collection de milliers de [A-Z]*.cfichiers générés (également avec un dx.hfichier d'en-tête généré ) avec une collection de milliers de _[0-9]*fichiers de données.

  • Plusieurs compilateurs Scheme sont également bootstrapés. Scheme48, Poulet Scheme, ...

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.