J'étudie intensivement les sujets des compilateurs et des interprètes. Je veux vérifier si ma compréhension de base est correcte, supposons donc ce qui suit:
J'ai une langue appelée "Foobish" et ses mots clés sont
<OUTPUT> 'TEXT', <Number_of_Repeats>;
Donc, si je veux imprimer 10 fois sur la console, j'écrirais
OUTPUT 'Hello World', 10;
Bonjour fichier World.foobish.
Maintenant j'écris un interprète dans la langue de mon choix - C # dans ce cas:
using System;
namespace FoobishInterpreter
{
internal class Program
{
private static void Main(string[] args)
{
analyseAndTokenize(Hello World.foobish-file)//Pseudocode
int repeats = Token[1];
string outputString = Token[0];
for (var i = 0; i < repeats; i++)
{
Console.WriteLine(outputString);
}
}
}
}
À un niveau d'interprète très facile, l'interprète analysait le fichier de script, etc. et exécutait le langage foobish de la manière de l'implémentation de l'interpréteur.
Un compilateur créerait-il un langage machine qui s'exécute directement sur le matériel physique?
Un interprète ne produit donc pas de langage machine, mais un compilateur le fait-il pour son entrée?
Dois-je me méprendre sur le fonctionnement de base des compilateurs et des interprètes?
Les termes "interprète" et "compilateur" sont beaucoup plus flous qu'avant. Il y a de nombreuses années, il était plus courant que les compilateurs produisent du code machine à exécuter plus tard, tandis que les interprètes "exécutaient" plus ou moins directement le code source. Ces deux termes étaient donc bien compris à l'époque.
Mais aujourd'hui, il existe de nombreuses variantes sur l'utilisation de "compilateur" et "interprète". Par exemple, VB6 "compile" en code octet (une forme de Intermediate Language ), qui est ensuite "interprété" par le Runtime VB. Un processus similaire a lieu en C #, qui produit CIL qui est ensuite exécuté par un Just-In-Time Compiler (JIT) qui, dans l'ancien jours, aurait été considéré comme un interprète. Vous pouvez "lyophiliser" la sortie du JIT dans un exécutable binaire réel en utilisant NGen.exe , dont le produit aurait été le résultat d'un compilateur dans l'ancien temps.
La réponse à votre question n'est donc pas aussi simple qu'elle l'était autrefois.
Lectures complémentaires
Compilateurs vs. Interprètes sur Wikipedia
Le résumé que je donne ci-dessous est basé sur "Compilers, Principles, Techniques, & Tools", Aho, Lam, Sethi, Ullman, (Pearson International Edition, 2007), pages 1, 2, avec l'ajout de quelques idées personnelles.
Les deux mécanismes de base pour traiter un programme sont compilation et interprétation.
La compilation prend en entrée un programme source dans une langue donnée et sort un programme cible dans une langue cible.
source program --> | compiler | --> target program
Si la langue cible est le code machine, elle peut être exécutée directement sur un processeur:
input --> | target program | --> output
La compilation implique l'analyse et la traduction de l'intégralité du programme d'entrée (ou du module) et n'implique pas son exécution.
L'interprétation prend en entrée le programme source et son entrée, et produit la sortie du programme source
source program, input --> | interpreter | --> output
L'interprétation implique généralement le traitement (analyse et exécution) du programme, une instruction à la fois.
Dans la pratique, de nombreux processeurs de langage utilisent un mélange des deux approches. Par exemple, Java sont d'abord traduits (compilés) en un programme intermédiaire (code d'octet):
source program --> | translator | --> intermediate program
la sortie de cette étape est ensuite exécutée (interprétée) par une machine virtuelle:
intermediate program + input --> | virtual machine | --> output
Pour compliquer encore les choses, la JVM peut effectuer une compilation juste à temps au moment de l'exécution pour convertir le code d'octet dans un autre format, qui est ensuite exécuté.
De plus, même lorsque vous compilez en langage machine, un interpréteur exécute votre fichier binaire qui est implémenté par le processeur sous-jacent. Par conséquent, même dans ce cas, vous utilisez un hybride de compilation + interprétation.
Ainsi, les systèmes réels utilisent un mélange des deux, il est donc difficile de dire si un processeur de langage donné est un compilateur ou un interprète, car il utilisera probablement les deux mécanismes à différentes étapes de son traitement. Dans ce cas, il serait probablement plus approprié d'utiliser un autre terme, plus neutre.
Néanmoins, la compilation et l'interprétation sont deux types distincts de traitement, comme décrit dans les schémas ci-dessus,
Pour répondre aux questions initiales.
Un compilateur créerait un langage machine qui s'exécute directement sur le matériel physique?
Pas nécessairement, un compilateur traduit un programme écrit pour une machine M1 en un programme équivalent écrit pour une machine M2. La machine cible peut être implémentée dans le matériel ou être une machine virtuelle. Sur le plan conceptuel, il n'y a aucune différence. Le point important est qu'un compilateur regarde un morceau de code et le traduit dans un autre langage sans l'exécuter.
Un interprète ne produit donc pas de langage machine mais un compilateur le fait pour son entrée?
Si en produisant vous faites référence à la sortie, alors un compilateur produit un programme cible qui peut être en langage machine, un interpréteur ne le fait pas.
Un compilateur créerait un langage machine
Non. Un compilateur est simplement un programme qui prend en entrée un programme écrit en langage [~ # ~] a [~ # ~] et produit comme sa sortie un programme sémantiquement équivalent dans la langue [~ # ~] b [~ # ~] . La langue [~ # ~] b [~ # ~] peut être n'importe quoi, il n'est pas nécessaire que ce soit un langage machine.
Un compilateur peut compiler d'un langage de haut niveau vers un autre langage de haut niveau (par exemple GWT, qui compile Java vers ECMAScript), d'un langage de haut niveau vers un langage de bas niveau (par exemple Gambit, qui compile Scheme en C), d'un langage de haut niveau au code machine (par exemple GCJ, qui compile Java en code natif), d'un langage de bas niveau à un niveau élevé (par exemple Clue, qui compile C en Java, Lua, Perl, ECMAScript et Common LISP), d'un langage de bas niveau à un autre langage de bas niveau (par exemple le Android SDK, qui compile Du bytecode JVML au bytecode Dalvik), d'un langage de bas niveau au code machine (par exemple le compilateur C1X qui fait partie de HotSpot, qui compile le bytecode JVML en code machine), du code machine en un langage de haut niveau (n'importe quel soi-disant " decompiler ", également Emscripten, qui compile le code machine LLVM en ECMAScript), le code machine en langage de bas niveau (par exemple le compilateur JIT en JPC, qui compile le code natif x86 en bytecode JVML) et le code natif au code natif (par exemple le compilateur JIT dans PearPC, qui compile le code natif PowerPC en code natif x86).
Notez également que "code machine" est un terme vraiment flou pour plusieurs raisons. Par exemple, il existe des CPU qui exécutent nativement le code d'octet JVM et il existe des interpréteurs logiciels pour le code machine x86. Alors, qu'est-ce qui fait un "code machine natif" mais pas l'autre? En outre, every language est le code d'une machine abstraite pour cette langue.
Il existe de nombreux noms spécialisés pour les compilateurs qui exécutent des fonctions spéciales. Malgré le fait que ce soient des noms spécialisés, ce sont toujours des compilateurs, juste des types spéciaux de compilateurs:
qui s'exécute directement sur le matériel physique?
Pas nécessairement. Il pourrait être exécuté dans un interpréteur ou dans une machine virtuelle. Il pourrait être ensuite compilé dans une autre langue.
Un interprète ne produit donc pas de langage machine mais un compilateur le fait pour son entrée?
Un interprète ne produit rien. Il exécute simplement le programme.
Un compilateur produit quelque chose, mais il ne doit pas nécessairement être un langage machine, il peut s'agir de n'importe quel langage. Il peut même s'agir de la même langue que la langue d'entrée! Par exemple, Supercompilers, LLC a un compilateur qui prend Java comme entrée et produit une sortie Java optimisée comme sortie. Il existe de nombreux compilateurs ECMAScript qui prennent ECMAScript comme leurs entrées et produire ECMAScript optimisé, minifié et obscurci comme leur sortie.
Vous pourriez également être intéressé par:
Je pense que vous devriez abandonner complètement la notion de "compilateur versus interprète", car c'est une fausse dichotomie.
Le mot collectif pour rendre un langage de programmation abstrait utile dans le monde réel est implémentation.
Dans le passé, une implémentation de langage de programmation consistait souvent en un simple compilateur (et le CPU pour lequel il générait du code) ou simplement un interprète - il peut donc avoir ressemblé ces deux types d'outils s'excluent mutuellement. Aujourd'hui, vous pouvez clairement voir que ce n'est pas le cas (et cela n'a jamais été pour commencer). Prendre une implémentation sophistiquée du langage de programmation et tenter de lui pousser le nom de "compilateur" ou "interprète" vous mènera souvent à des résultats peu concluants ou incohérents.
Une implémentation de langage de programmation unique peut impliquer n'importe quel nombre de compilateurs et d'interprètes, souvent sous plusieurs formes (autonome, à la volée), un certain nombre d'autres outils, comme analyseurs statiques = et optimiseurs, et n'importe quel nombre d'étapes. Il peut même inclure des implémentations entières de n'importe quel nombre de langages intermédiaires (qui peuvent être sans rapport avec celui qui est implémenté).
Exemples de schémas de mise en œuvre:
...etc.
Alors que les lignes entre les compilateurs et les interprètes sont devenues floues au fil du temps, on peut toujours tracer une ligne entre elles en examinant la sémantique de ce que le programme devrait faire et de ce que fait le compilateur/interprète.
Un compilateur générera un autre programme (généralement dans un langage de niveau inférieur comme le code machine) qui, si ce programme est exécuté, fera ce que votre programme devrait faire.
Un interprète fera ce que votre programme devrait faire.
Avec ces définitions, les endroits où cela devient flou sont les cas où votre compilateur/interprète peut être considéré comme faisant des choses différentes selon la façon dont vous le regardez. Par exemple, Python prend votre Python et le compile dans un fichier compilé Python bytecode. Si cet Python bytecode est exécuté via un interpréteur Python bytecode , il fait ce que votre programme était censé faire. Cependant, dans la plupart des situations, les développeurs Python pensent que ces deux étapes se font en une seule étape, ils choisissent donc de penser à l'interpréteur CPython comme interprétant leur code source, et au fait qu'il a obtenu compilé en cours de route est considéré comme un détail d'implémentation. De cette façon, tout est une question de perspective.
Voici une simple désambiguïsation conceptuelle entre les compilateurs et les interprètes.
Considérez 3 langues: programmation langue, P (dans quoi le programme est écrit); domaine langue, D (pour ce qui se passe avec le programme en cours); et cible langue, T (une troisième langue).
Conceptuellement,
un compilateur traduit P en T afin que vous puissiez évaluer T (D); tandis que
un interprète évalue directement P(D).