Les compilateurs avancés, par exemple, gcc
compilent les codes dans des fichiers lisibles par machine en fonction du langage dans lequel le code a été écrit (par exemple C, C ++, etc.). En fait, ils interprètent la signification de chaque code en fonction de la bibliothèque et des fonctions des langages correspondants. Corrige moi si je me trompe.
Je souhaite mieux comprendre les compilateurs en écrivant un compilateur très basique (probablement en C) pour compiler un fichier statique (par exemple, Hello World dans un fichier texte). J'ai essayé des tutoriels et des livres, mais tous sont des cas pratiques. Ils traitent de la compilation de codes dynamiques avec des significations liées au langage correspondant.
Comment puis-je écrire un compilateur de base pour convertir un texte statique en un fichier lisible par machine?
La prochaine étape consistera à introduire des variables dans le compilateur. imaginons que nous voulions écrire un compilateur qui ne compile que certaines fonctions d’un langage.
L'introduction de tutoriels et de ressources pratiques est très appréciée :-)
la source
Réponses:
Intro
Un compilateur typique effectue les étapes suivantes:
La plupart des compilateurs modernes (par exemple, gcc et clang) répètent encore une fois les deux dernières étapes. Ils utilisent un langage intermédiaire de bas niveau mais indépendant de la plate-forme pour la génération initiale du code. Ensuite, cette langue est convertie en code spécifique à la plate-forme (x86, ARM, etc.) faisant à peu près la même chose d'une manière optimisée pour la plate-forme. Cela inclut, par exemple, l'utilisation d'instructions vectorielles lorsque cela est possible, la réorganisation d'instructions pour augmenter l'efficacité de la prédiction de branche, etc.
Après cela, le code objet est prêt pour la liaison. La plupart des compilateurs de code natif savent comment appeler un éditeur de liens pour produire un exécutable, mais ce n'est pas une étape de compilation en soi. Dans des langages tels que Java et C #, la liaison peut être totalement dynamique, effectuée par la VM au moment du chargement.
Rappelez-vous les bases
Cette séquence classique s’applique à tous les développements logiciels, mais elle mérite d’être répétée.
Concentrez-vous sur la première étape de la séquence. Créez la chose la plus simple qui puisse fonctionner.
Lisez les livres!
Lisez le livre du dragon par Aho et Ullman. Ceci est classique et est encore tout à fait applicable aujourd'hui.
La conception du compilateur moderne est également appréciée.
Si cela vous pose trop de problèmes en ce moment, lisez d'abord quelques intros sur l'analyse; Les bibliothèques d'analyse comprennent généralement des intros et des exemples.
Assurez-vous que vous êtes à l'aise avec les graphiques, en particulier les arbres. Ces choses sont les choses dont les programmes sont faits au niveau logique.
Définissez bien votre langue
Utilisez la notation que vous voulez, mais assurez-vous d'avoir une description complète et cohérente de votre langue. Cela inclut à la fois la syntaxe et la sémantique.
Il est grand temps d'écrire des extraits de code dans votre nouvelle langue en tant que cas de test pour le futur compilateur.
Utilisez votre langue préférée
Écrire un compilateur en Python, en Ruby ou dans n’importe quel langage qui vous convient est tout à fait acceptable. Utilisez des algorithmes simples que vous comprenez bien. La première version ne doit pas nécessairement être rapide, efficace ou complète. Il doit seulement être suffisamment correct et facile à modifier.
Il est également correct d'écrire différentes étapes d'un compilateur dans différentes langues, si nécessaire.
Préparez-vous à écrire beaucoup de tests
Toute votre langue devrait être couverte par des cas de test; effectivement, il sera défini par eux. Familiarisez-vous avec votre framework de test préféré. Écrire des tests dès le premier jour. Concentrez-vous sur les tests «positifs» acceptant le code correct, par opposition à la détection de code incorrect.
Exécutez tous les tests régulièrement. Corrigez les tests brisés avant de continuer. Il serait dommage de se retrouver avec un langage mal défini qui ne puisse accepter un code valide.
Créer un bon analyseur
Les générateurs de parseurs sont nombreux . Choisissez ce que vous voulez. Vous pouvez également écrire votre propre analyseur à partir de rien, mais cela ne vaut que si la syntaxe de votre langue est extrêmement simple.
L'analyseur doit détecter et signaler les erreurs de syntaxe. Écrivez beaucoup de cas tests, à la fois positifs et négatifs; réutilisez le code que vous avez écrit en définissant la langue.
La sortie de votre analyseur est un arbre de syntaxe abstraite.
Si votre langage comporte des modules, la sortie de l'analyseur peut être la représentation la plus simple du "code objet" que vous générez. Il existe de nombreuses façons simples de déposer un arbre dans un fichier et de le recharger rapidement.
Créer un validateur sémantique
Très probablement, votre langage permet des constructions syntaxiquement correctes qui peuvent ne pas avoir de sens dans certains contextes. Un exemple est une déclaration en double de la même variable ou la transmission d'un paramètre d'un type incorrect. Le validateur détectera de telles erreurs en regardant l’arbre.
Le validateur résoudra également les références à d'autres modules écrits dans votre langue, chargera ces autres modules et les utilisera dans le processus de validation. Par exemple, cette étape s'assurera que le nombre de paramètres transmis à une fonction par un autre module est correct.
Encore une fois, écrivez et exécutez beaucoup de cas de test. Les cas triviaux sont aussi indispensables au dépannage que intelligents et complexes.
Générer du code
Utilisez les techniques les plus simples que vous connaissez. Il est souvent correct de traduire directement une construction de langage (comme une
if
instruction) en un modèle de code légèrement paramétré, semblable à un modèle HTML.Encore une fois, ignorez l'efficacité et concentrez-vous sur la correction.
Ciblez une machine virtuelle de bas niveau indépendante de la plate-forme
Je suppose que vous ignorez les éléments de bas niveau, à moins que vous ne vous intéressiez vraiment aux détails spécifiques au matériel. Ces détails sont sanglants et complexes.
Vos options:
Ignorer l'optimisation
L'optimisation est difficile. Presque toujours, l'optimisation est prématurée. Générer un code inefficace mais correct. Implémentez l'ensemble du langage avant d'essayer d'optimiser le code résultant.
Bien sûr, des optimisations triviales sont acceptables. Mais évitez toute substance rusée et poilue avant que votre compilateur ne soit stable.
Et alors?
Si tout cela ne vous intimide pas trop, continuez! Pour un langage simple, chacune des étapes peut être plus simple que vous ne le pensez.
Voir un «bonjour» à partir d’un programme créé par votre compilateur pourrait en valoir la peine.
la source
Construisons un compilateur de Jack Crenshaw , bien qu'inachevé, est une introduction et un tutoriel extrêmement lisibles.
Nicklaus Wirth's Compiler Construction est un très bon manuel sur les bases de la construction d'un compilateur simple. Il se concentre sur la descente récursive de haut en bas, ce qui, soyons honnêtes, est BEAUCOUP plus facile que le lex / yacc ou le flex / bison. Le compilateur PASCAL original que son groupe a écrit a été réalisé de cette façon.
D'autres personnes ont mentionné les différents livres de Dragon.
la source
En fait, je commencerais par écrire un compilateur pour Brainfuck . C'est un langage assez obtus pour programmer, mais il n'a que 8 instructions à mettre en œuvre. C'est à peu près aussi simple que possible et il existe des instructions C équivalentes pour les commandes impliquées si vous trouvez que la syntaxe est déroutante.
la source
Si vous voulez vraiment écrire uniquement du code lisible par machine et non destiné à une machine virtuelle, vous devrez lire les manuels Intel et comprendre.
une. Liaison et chargement de code exécutable
b. Formats COFF et PE (pour Windows), sinon comprendre le format ELF (pour Linux)
Beaucoup plus difficile que dit. Je vous suggère de lire Compilers and Interpreters en C ++ comme point de départ (par Ronald Mak). Sinon, "permet de construire un compilateur" par Crenshaw est OK.
Si vous ne le souhaitez pas, vous pouvez également écrire votre propre machine virtuelle et écrire un générateur de code destiné à cette machine virtuelle.
Conseils: Apprendre Flex et Bison EN PREMIER. Continuez ensuite à construire votre propre compilateur / VM.
Bonne chance!
la source
L'approche de bricolage pour un compilateur simple pourrait ressembler à ceci (du moins, c'est à ça que ressemblait mon projet uni):
Il devrait y avoir beaucoup de littérature décrivant chaque étape en détail.
la source