Un interprète produit-il un code machine?

42

J'étudie les sujets des compilateurs et des interprètes de manière intensive. Je veux vérifier si ma compréhension de base est correcte, alors supposons 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 World.foobish-file.

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);
            }
        }
    }
}

Sur un niveau d'interprète très facile, l'interprète analysera le fichier script, etc. et exécutera le langage foobish de la même manière que l'implémentation de l'interprète.

Un compilateur créerait-il un langage machine qui fonctionne directement sur le matériel physique?

Donc, un interprète ne produit pas de langage machine, mais un compilateur le fait-il pour son entrée?

Est-ce que j'ai des malentendus dans la façon dont fonctionne les compilateurs et interprètes?

GrayFox
la source
21
Que pensez-vous du "compilateur" C #? Comme indice, il ne produit pas de code machine.
Philip Kendall
3
Un compilateur Java produit du code pour la machine virtuelle Java. Ainsi, la machine cible d'un compilateur peut être une machine virtuelle qui n'est pas exécutée directement par le matériel. La principale différence entre interprète et compilateur réside dans le fait qu’un compilateur vérifie d’abord et traduit tout le code source dans un langage machine cible. Ce code compilé est ensuite exécuté par la machine à laquelle il était destiné. D'autre part, un interprète traduira et exécutera des morceaux de votre programme à la volée.
Giorgio
@Giorgio: Vous voulez dire, comme un JIT?
Robert Harvey
2
@RobertHarvey: Je voulais dire le compilateur Java (javac): autant que je sache, il produit un code intermédiaire pour la machine virtuelle Java. Et, encore une fois, autant que je sache, le JIT postérieurement (au moment de l’exécution) compile un bytecode qui est utilisé très souvent dans le langage de la machine native.
Giorgio
4
un compilateur signifie traduire. Il peut émettre toutes sortes de langages: c, assemblage, javascript, code machine.
Esben Skov Pedersen

Réponses:

77

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 ultérieurement, tandis que les interprètes "exécutaient" directement le code source. Donc, ces deux termes étaient bien compris à l'époque.

Mais aujourd'hui, il y a beaucoup de variations sur l'utilisation de "compilateur" et "interprète". Par exemple, VB6 "compile" en code d'octet (une forme de langage intermédiaire ), qui est ensuite "interprété" par VB Runtime. Un processus similaire a lieu en C #, ce qui produit CIL qui est ensuite exécuté par un compilateur juste-à-temps (JIT) qui, jadis, aurait été considéré comme un interprète. Vous pouvez "lyophiliser" la sortie du JIT dans un exécutable binaire en utilisant NGen.exe , dont le produit aurait été le résultat d'un compilateur de l'ancien temps.

La réponse à votre question n’est donc pas aussi simple qu’elle l’était.

Lectures complémentaires
Compilateurs vs interprètes sur Wikipédia

Robert Harvey
la source
6
@Giorgio: De nos jours, la plupart des interprètes n'exécutent pas le code source, mais plutôt la sortie d'un fichier AST ou similaire. Les compilateurs ont un processus similaire. La distinction n’est pas aussi nette que vous le pensez.
Robert Harvey
5
"Vous pouvez" geler "la sortie du JIT dans un exécutable binaire en utilisant NGen.exe, dont le produit aurait été le résultat d'un compilateur dans le passé.": Mais c'est toujours le résultat aujourd'hui d'un compilateur (à savoir, le compilateur juste à temps). Peu importe quand le compilateur est lancé, mais ce qu'il fait. Un compilateur prend en entrée une représentation d'un morceau de code et génère une nouvelle représentation. Un interprète affichera le résultat de l'exécution de ce morceau de code. Ce sont deux processus différents, peu importe comment vous les mélangez et quand vous exécutez quoi.
Giorgio
4
"Compilateur" est simplement le terme qu’ils ont choisi d’attacher à GCC. Ils ont choisi de ne pas appeler NGen de compilateur, même s’il produisait du code machine, préférant lier ce terme à l’étape précédente, ce qui pourrait sans doute être appelé un interprète, même s’il produisait du code machine (certains interprètes le font également). Ce que je veux dire, c’est qu’aujourd’hui, il n’existe aucun principe contraignant que l’on puisse invoquer pour appeler définitivement un compilateur ou un interprète, autre que "c’est ce qu’ils ont toujours appelé".
Robert Harvey
4
Selon ma compréhension très limitée, de nos jours, les processeurs x86 sont à mi-chemin de toute façon comme des moteurs JIT basés sur du matériel, l'assemblage ayant une relation de plus en plus fade avec ce qui est exécuté exactement.
Leushenko
4
@RobertHarvey Bien que je convienne qu'il n'y a pas de distinction nette entre les techniques utilisées dans un interpréteur et un compilateur, il existe une division assez claire dans la fonction: si le résultat de l'exécution d'un outil donné avec le code d'un programme en entrée est l'exécution de celui-ci. programme, l’outil est un interprète. Si le résultat est la sortie d'une traduction du programme sous une forme moins abstraite, il s'agit d'un compilateur. Si le résultat est une traduction en une forme plus abstraite, c'est un décompilateur. Les cas où plus d'un de ces résultats sont ambigus, cependant.
Jules
34

Le résumé que je donne ci-dessous est basé sur "Compilateurs, principes, techniques et outils", Aho, Lam, Sethi, Ullman, (Pearson International Edition, 2007), pages 1 et 2, avec l’ajout de certaines de mes idées personnelles.

Les deux mécanismes de base pour le traitement d'un programme sont la compilation et l' interprétation .

La compilation prend en entrée un programme source dans une langue donnée et génère un programme cible dans une langue cible.

source program --> | compiler | --> target program

Si la langue cible est le code machine, il peut être exécuté directement sur un processeur:

input --> | target program | --> output

La compilation implique la numérisation et la traduction de l’ensemble du programme d’entrée (ou du module) et ne nécessite 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 (l'analyse et l'exécution) du programme, une instruction à la fois.

En pratique, de nombreux processeurs de langage utilisent une combinaison des deux approches. Par exemple, les programmes Java sont d'abord traduits (compilés) dans un programme intermédiaire (code 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 machine virtuelle Java peut effectuer une compilation juste à temps au moment de l'exécution afin de convertir le code octet dans un autre format, qui est ensuite exécuté.

En outre, même lorsque vous compilez en langage machine, un interpréteur exécutant votre fichier binaire est implémenté par le processeur sous-jacent. Par conséquent, même dans ce cas, vous utilisez un système hybride compilation + interprétation.

Ainsi, les systèmes réels utilisent une combinaison 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 de traitement distincts, décrits dans les schémas ci-dessus,

Pour répondre aux questions initiales.

Un compilateur créerait un langage machine qui fonctionne 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 matériellement ou être une machine virtuelle. Conceptuellement, il n'y a pas de différence. L'important est qu'un compilateur examine un morceau de code et le traduise dans un autre langage sans l'exécuter.

Donc, un interprète ne produit pas de langage machine, mais un compilateur le fait pour son entrée?

Si, en produisant, vous faites référence à la sortie, un compilateur produit un programme cible qui peut être en langage machine, contrairement à un interprète.

Giorgio
la source
7
En d'autres termes: un interprète prend un programme P et produit sa sortie O, un compilateur prend P et produit un programme P 'qui fournit O; les interprètes incluent souvent des composants qui sont des compilateurs (par exemple, en un bytecode, une représentation intermédiaire ou des instructions machine JIT) et de même, un compilateur peut inclure un interpréteur (par exemple, pour évaluer des calculs au moment de la compilation).
Jon Purdy
"un compilateur peut inclure un interpréteur (par exemple, pour évaluer des calculs au moment de la compilation)": bon point. Je suppose que les macros Lisp et les modèles C ++ pourraient être pré-traités de cette manière.
Giorgio
Encore plus simple, le préprocesseur C compile le code source C avec les directives CPP en C simple et inclut un interpréteur pour les expressions booléennes telles que defined A && !defined B.
Jon Purdy
@JonPurdy Je suis d'accord avec cela, mais j'ajouterais également une classe, "interprètes traditionnels", qui n'utilise pas de représentations intermédiaires au-delà d'une version éventuellement symbolisée de la source. Des exemples sont les shells, de nombreux BASIC, Lisp classique, Tcl avant 8.0 et bc.
Hobbs
1
@naxa - voir la réponse de Lawrence et les commentaires de Paul Draper sur les types de compilateur. Un assembleur est un type spécial de compilateur où (1) le langage de sortie est destiné à une exécution directe par une machine ou une machine virtuelle et (2) il existe une correspondance simple entre deux instructions à la place des instructions d'entrée et des instructions de sortie.
Jules
22

Un compilateur créerait un langage machine

Non . Un compilateur est tout simplement un programme qui prend en entrée un programme écrit en langage A et produit en sortie un programme sémantiquement équivalent dans le langage B . La langue B peut être n'importe quoi, il n'est pas nécessaire qu'elle soit en langage machine.

Un compilateur peut compiler d'un langage de haut niveau à un autre langage de haut niveau (par exemple, GWT, qui compile Java vers ECMAScript), d'un langage de haut niveau à 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 langage de haut niveau (par exemple, Clue, qui compile le C en Java, Lua, Perl, ECMAScript et Common Lisp), d’un langage de bas niveau à un autre langage de bas niveau (par exemple, le SDK Android, qui compile le bytecode JVML en Daltik), 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), le code machine en un langage évolué (tout "décompilateur", également Emscripten, qui compile le code machine LLVM en ECMAScript),code machine en langage de bas niveau (par exemple, le compilateur JIT dans JPC, qui compile le code natif x86 en bytecode JVML) et le code natif en 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 très flou pour plusieurs raisons. Par exemple, certains processeurs exécutent de manière native le code octet JVM et des interpréteurs logiciels pour le code machine x86. Alors, qu'est-ce qui fait qu'un "code machine natif" mais pas l'autre? De plus, chaque langue est un code pour une machine abstraite pour cette langue.

Il existe de nombreux noms spécialisés pour les compilateurs qui remplissent des fonctions spéciales. Bien qu'il s'agisse de noms spécialisés, tous restent des compilateurs, mais des types de compilateurs particuliers:

  • si le langage A est perçu comme ayant à peu près le même niveau d'abstraction que le langage B , le compilateur peut être appelé transpiler (par exemple, un transpiler Ruby-to-ECMAScript ou un ECMAScript2015-en-ECMAScript5).
  • si le langage A est perçu comme ayant un niveau d'abstraction inférieur au langage B , le compilateur peut être appelé un décompilateur (par exemple un décompilateur x86-machine-code-to-C)
  • si langage A == langage B , le compilateur peut être appelé optimiseur , obfuscateur ou minificateur (selon la fonction particulière du compilateur)

qui fonctionne directement sur le matériel physique?

Pas nécessairement. Il peut être exécuté dans un interprète ou dans une machine virtuelle. Il pourrait être ensuite compilé dans une langue différente.

Donc, un interprète ne produit pas de langage machine, mais un compilateur le fait pour son entrée?

Un interprète ne produit rien. Il ne fait que lancer le programme.

Un compilateur produit quelque chose, mais il ne doit pas nécessairement s'agir d'un langage machine, il peut s'agir de n'importe quel langage. Ce peut même être la même langue que la langue d'entrée! Par exemple, Supercompilers, LLC a un compilateur qui prend Java en entrée et produit une sortie Java optimisée. Il existe de nombreux compilateurs ECMAScript qui utilisent ECMAScript en entrée et produisent en sortie un ECMAScript optimisé, minimisé et obscurci.


Vous pouvez également être intéressé par:

Jörg W Mittag
la source
16

Je pense que vous devriez abandonner complètement la notion de "compilateur par rapport à interprète", car c'est une fausse dichotomie.

  • Un compilateur est un transformateur : il transforme un programme informatique écrit dans un langage source et génère un équivalent dans un langage cible . Habituellement, la langue source est de niveau supérieur à la langue cible - et si c'est l'inverse, nous appelons souvent ce type de transformateur un décompilateur .
  • Un interprète est un moteur d'exécution . Il exécute un programme informatique écrit dans une langue, conformément aux spécifications de cette langue. Nous utilisons principalement le terme de logiciel (mais d'une certaine manière, un processeur classique peut être considéré comme un "interprète" basé sur le matériel pour son code machine).

Le mot collectif pour rendre un langage de programmation abstrait utile dans le monde réel est implémentation .

Auparavant, une implémentation de langage de programmation consistait souvent en un compilateur (et le processeur pour lequel elle avait généré le code) ou en un interpréteur. Il est donc possible que ces deux types d’outils s’excluent mutuellement. Aujourd'hui, vous pouvez voir clairement que ce n'est pas le cas (et cela n'a jamais été le cas). Prendre une implémentation sophistiquée du langage de programmation et tenter de lui attribuer le nom de "compilateur" ou "interprète" vous mènera souvent à des résultats peu concluants ou incohérents.

Une seule implémentation de langage de programmation peut impliquer un nombre illimité de compilateurs et d'interprètes , souvent sous plusieurs formes (autonome, à la volée), un nombre illimité d'outils, tels que des analyseurs statiques et des optimiseurs , et un nombre illimité d'étapes. Il peut même inclure des implémentations complètes d'un nombre quelconque de langages intermédiaires (qui peuvent être sans rapport avec celui qui est implémenté).

Voici des exemples de schémas de mise en œuvre:

  • Compilateur CA qui transforme le code machine C en code x86 et un processeur x86 qui exécute ce code.
  • Compilateur CA qui transforme C en LLVM IR, un compilateur backend LLVM qui transforme LLVM IR en code machine x86 et un CPU x86 qui exécute ce code.
  • Compilateur CA qui transforme C en LLVM IR et un interpréteur LLVM qui exécute LLVM IR.
  • Un compilateur Java qui transforme Java en bytecode JVM et un JRE avec un interpréteur qui exécute ce code.
  • Un compilateur Java qui transforme Java en bytecode JVM et un JRE avec à la fois un interpréteur exécutant certaines parties de ce code et un compilateur transformant d'autres parties de ce code en code machine x86 et un processeur x86 exécutant ce code.
  • Un compilateur Java qui transforme Java en bytecode JVM et un processeur ARM qui exécute ce code.
  • AC # compilateur qui transforme C # en CIL, un CLR avec un compilateur qui transforme CIL en code machine x86 et un processeur x86 qui exécute ce code.
  • Un interprète Ruby qui exécute Ruby.
  • Un environnement Ruby avec à la fois un interpréteur qui exécute Ruby et un compilateur qui transforme Ruby en code machine x86, et un processeur x86 qui exécute ce code.

...etc.

Theodoros Chatzigiannakis
la source
+1 pour signaler que même les codages conçus pour une représentation intermédiaire (par exemple, bytecode java) peuvent avoir des implémentations matérielles.
Jules
7

Bien que les lignes entre les compilateurs et les interprètes soient devenues floues au fil du temps, on peut toujours les distinguer en examinant la sémantique de ce que le programme devrait faire et du compilateur / interprète.

Un compilateur générera un autre programme (généralement dans un langage de niveau inférieur tel que 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 différentes choses en fonction de la façon dont vous le regardez. Par exemple, Python prend votre code Python et le compile en un bytecode Python compilé. Si ce bytecode Python est exécuté via un interpréteur de bytecode Python , il fait ce que votre programme était censé faire. Cependant, dans la plupart des situations, les développeurs Python pensent que ces deux étapes doivent être exécutées en une seule étape. Ils choisissent donc de considérer l' interpréteur CPython comme une interprétation de leur code source. Le fait qu'il ait été 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.

Cort Ammon
la source
5

Voici une simple homonymie conceptuelle entre les compilateurs et les interprètes.

Considérez 3 langages: le langage de programmation , P (dans lequel le programme est écrit); langue du domaine , D (pour ce qui se passe avec le programme en cours d'exécution); et langue cible , 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).

Lawrence
la source
1
La plupart des interprètes modernes n'évaluent pas directement la langue source, mais plutôt une représentation intermédiaire de la langue source.
Robert Harvey
4
@RobertHarvey Cela ne change pas la distinction conceptuelle entre les termes.
Lawrence
1
Donc, ce que vous appelez réellement l'interprète est la partie qui évalue la représentation intermédiaire. La partie qui crée la représentation intermédiaire est un compilateur , selon votre définition.
Robert Harvey
6
@ RobertHarvey Pas vraiment. Les termes dépendent du niveau d'abstraction sur lequel vous travaillez. Si vous regardez en dessous, l'outil pourrait faire n'importe quoi. Par analogie, disons que vous allez dans un pays étranger et que vous emmenez un ami bilingue, Bob. Si vous communiquez avec les sections locales en parlant à Bob qui, à son tour, s’entretient avec les sections locales, il vous servira d’interprète (même s’il gribouille dans leur langue avant de parler). Si vous demandez des phrases à Bob et que Bob les écrit dans la langue étrangère, et que vous communiquez avec les habitants en vous référant à ces écrits (pas à Bob), Bob agit en tant que compilateur pour vous.
Lawrence
1
Excellente réponse. À noter: de nos jours, vous pouvez entendre "transpiler". C'est un compilateur où P et T ont des niveaux d'abstraction similaires, pour une définition de similaire. (Par exemple, un transpiler ES5 à ES6.)
Paul Draper