Un interprete produce un codice macchina?

42

Studio intensamente i temi dei compilatori e degli interpreti. Voglio verificare se la mia comprensione di base è corretta, quindi assumiamo quanto segue:

Ho una lingua chiamata "Foobish" e le sue parole chiave sono

<OUTPUT> 'TEXT', <Number_of_Repeats>;

Quindi, se voglio stampare sulla console 10 volte, vorrei scrivere

OUTPUT 'Hello World', 10;

Ciao World.foobish-file.

Ora scrivo un interprete nella lingua che preferisco - C # in questo caso:

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

Su un livello di interprete molto semplice, l'interprete analizzerebbe il file di script, ecc. ed eseguirà il linguaggio folle nella maniera dell'implementazione dell'interprete.

Un compilatore creerebbe un linguaggio macchina eseguito direttamente sull'hardware fisico?

Quindi un interprete non produce un linguaggio macchina, ma un compilatore lo fa per il suo input?

Ho qualche malinteso nel modo basilare su come funzionano i compilatori e gli interpreti?

    
posta GrayFox 22.10.2015 - 19:35
fonte

6 risposte

79

I termini "interprete" e "compilatore" sono molto più sfocati di un tempo. Molti anni fa era più comune per i compilatori produrre codice macchina da eseguire successivamente, mentre gli interpreti più o meno "eseguivano" direttamente il codice sorgente. Quindi quei due termini erano ben compresi allora.

Ma oggi ci sono molte varianti sull'uso di "compilatore" e "interprete". Ad esempio, VB6 "compila" in byte code (una forma di Lingua intermedia ), che viene quindi "interpretata" dal VB Runtime. Un processo simile avviene in C #, che produce CIL che viene quindi eseguito da un Just-In-Time Compiler (JIT) che, ai vecchi tempi, sarebbe stato pensato come un interprete. È possibile "congelare" l'output del JIT in un eseguibile binario effettivo utilizzando NGen.exe , il cui prodotto sarebbe stato il risultato di un compilatore ai vecchi tempi.

Quindi la risposta alla tua domanda non è così semplice come una volta.

Ulteriori letture
Compilatori contro interpreti su Wikipedia

    
risposta data 22.10.2015 - 20:10
fonte
35

Il sommario che riporto di seguito è basato su "Compilatori, principi, tecniche e strumenti", Aho, Lam, Sethi, Ullman, (Pearson International Edition, 2007), pagine 1, 2, con l'aggiunta di alcune idee di mio.

I due meccanismi di base per l'elaborazione di un programma sono compilation e interpretazione .

La compilazione prende come input un programma sorgente in una determinata lingua e genera un programma target in una lingua di destinazione.

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

Se la lingua di destinazione è un codice macchina, può essere eseguita direttamente su qualche processore:

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

La compilazione implica la scansione e la traduzione dell'intero programma di input (o modulo) e non implica l'esecuzione.

L'interpretazione prende come input il programma sorgente e il suo input, e produce l'output del programma sorgente

source program, input --> | interpreter | --> output

L'interpretazione di solito comporta l'elaborazione (analisi ed esecuzione) del programma, una dichiarazione alla volta.

In pratica, molti processori linguistici utilizzano un mix dei due approcci. Ad esempio, i programmi Java vengono prima tradotti (compilati) in un programma intermedio (byte code):

source program --> | translator | --> intermediate program

l'output di questo passaggio viene quindi eseguito (interpretato) da una macchina virtuale:

intermediate program + input --> | virtual machine | --> output

Per complicare ulteriormente le cose, la JVM può eseguire una compilazione just-in-time in runtime per convertire il codice byte in un altro formato, che viene poi eseguito.

Inoltre, anche quando si compila il linguaggio macchina, c'è un interprete che esegue il file binario implementato dal processore sottostante. Pertanto, anche in questo caso stai utilizzando un ibrido di compilation + interpretazione.

Quindi, i sistemi reali usano un mix dei due, quindi è difficile dire se un determinato processore di linguaggio è un compilatore o un interprete, perché probabilmente utilizzerà entrambi i meccanismi nelle diverse fasi della sua elaborazione. In questo caso sarebbe probabilmente più appropriato usare un altro termine più neutro.

Tuttavia, la compilazione e l'interpretazione sono due tipi distinti di elaborazione, come descritto nei diagrammi sopra,

Per rispondere alle domande iniziali.

A compiler would create machine language which runs on the physical hardware directly?

Non necessariamente, un compilatore traduce un programma scritto per una macchina M1 in un programma equivalente scritto per una macchina M2. La macchina target può essere implementata in hardware o essere una macchina virtuale. Concettualmente non c'è differenza. Il punto importante è che un compilatore guarda un pezzo di codice e lo traduce in un'altra lingua senza eseguirlo.

So an interpreter doesn't produce machine language but a compiler does it for its input?

Se per produrre ti riferisci all'output, allora un compilatore produce un programma target che può essere in linguaggio macchina, un interprete no.

    
risposta data 22.10.2015 - 23:05
fonte
22

A compiler would create machine language

No. Un compilatore è semplicemente un programma che prende come input un programma scritto in linguaggio A e produce come output un programma semanticamente equivalente nella lingua B . La lingua B può essere qualsiasi cosa, non deve essere linguaggio macchina.

Un compilatore può compilare da un linguaggio di alto livello ad un altro linguaggio di alto livello (es. GWT, che compila da Java a ECMAScript), da un linguaggio di alto livello ad un linguaggio di basso livello (es. Gambit, che compila Scheme per C), da un linguaggio di alto livello a un codice macchina (es. GCJ, che compila Java al codice nativo), da un linguaggio di basso livello ad un linguaggio di alto livello (es. Clue, che compila da C a Java, Lua, Perl, ECMAScript e Common Lisp), da un linguaggio di basso livello ad un altro linguaggio di basso livello (ad esempio l'SDK di Android, che compila il bytecode JVML al bytecode di Dalvik), da un linguaggio di basso livello a un codice macchina (ad esempio il compilatore C1X che è parte di HotSpot, che compila codice bytecode JVML in codice macchina), codice macchina in un linguaggio di alto livello (qualsiasi cosiddetto "decompilatore", anche Emscripten, che compila codice macchina LLVM in ECMAScript), codice macchina in linguaggio di basso livello (ad es. il compilatore JIT in JPC, che compila il codice nativo x86 in bytecode JVML) e il codice nativo in codice nativo (ad es. Compilatore JIT in PearPC, che compila il codice nativo di PowerPC nel codice nativo x86).

Si noti inoltre che "codice macchina" è un termine davvero confuso per diversi motivi. Ad esempio, ci sono CPU che eseguono in modo nativo il codice byte JVM e ci sono interpreti software per codice macchina x86. Quindi, cosa rende un "codice macchina nativo" ma non l'altro? Inoltre, ogni lingua è il codice per una macchina astratta per quella lingua.

Esistono molti nomi specializzati per i compilatori che eseguono funzioni speciali. Nonostante si tratti di nomi specializzati, tutti questi sono ancora compilatori, solo tipi speciali di compilatori:

  • se la lingua A è percepita come all'incirca allo stesso livello di astrazione del linguaggio B , il compilatore potrebbe essere chiamato transpiler ( es. un transpiler Ruby-to-ECMAScript o un transpiler ECMAScript2015-to-ECMAScript5
  • se la lingua A è percepita come ad un livello di astrazione di livello inferiore rispetto al linguaggio B , il compilatore potrebbe essere chiamato decompiler ( ad es. un decompilatore x86-machine-code-a-C)
  • se lingua A == lingua B , il compilatore potrebbe essere chiamato optimizer , obfuscator o minifier (a seconda della particolare funzione del compilatore)

which runs on the physical hardware directly?

Non necessariamente. Potrebbe essere eseguito in un interprete o in una VM. Potrebbe essere ulteriormente compilato in una lingua diversa.

So an interpreter doesn't produce machine language but a compiler does it for its input?

Un interprete non produce nulla. Esegue semplicemente il programma.

Un compilatore produce qualcosa, ma non deve necessariamente essere linguaggio macchina, può essere qualsiasi lingua. Può anche essere la stessa lingua della lingua di input! Ad esempio, Supercompilers, LLC ha un compilatore che prende come input Java e produce Java ottimizzato come output. Esistono molti compilatori ECMAScript che accettano ECMAScript come input e producono ECMAScript ottimizzato, miniato e offuscato come output.

Potresti anche essere interessato a:

risposta data 23.10.2015 - 09:23
fonte
15

Penso che dovresti abbandonare completamente la nozione di "interprete versus ", perché è una falsa dicotomia.

  • Un compilatore è un trasformatore : trasforma un programma per computer scritto in un linguaggio sorgente e genera un equivalente in un target lingua . Di solito, la lingua di partenza è di livello superiore rispetto alla lingua di destinazione e, se è il contrario, spesso chiamiamo quel tipo di trasformatore un decompilatore .
  • Un interprete è un motore di esecuzione . Esegue un programma per computer scritto in una lingua, secondo le specifiche di quella lingua. Usiamo principalmente il termine per il software (ma in un certo senso, una CPU classica può essere vista come un "interprete" basato su hardware per il suo codice macchina).

La parola collettiva per fare un linguaggio di programmazione astratto utile nel mondo reale è implementazione .

In passato, un'implementazione del linguaggio di programmazione consisteva spesso solo in un compilatore (e nella CPU per la quale generava il codice) o semplicemente in un interprete - quindi potrebbe avere come questi due tipi di strumenti sono si escludono a vicenda. Oggi puoi vedere chiaramente che non è questo il caso (e non è mai stato il caso di cominciare). Prendere un'implementazione sofisticata del linguaggio di programmazione e tentare di forzare il nome "compiler" o "interprete", spesso ti porterà a risultati inconcludenti o incoerenti.

Una singola implementazione del linguaggio di programmazione può coinvolgere un numero qualsiasi di compilatori e interpreti , spesso in più forme (standalone, al volo), qualsiasi numero di altri strumenti, come analizzatori statici e ottimizzatori e qualsiasi numero di passaggi. Può persino includere intere implementazioni di un qualsiasi numero di lingue intermedie (che potrebbero non essere correlate a quella in fase di implementazione).

Esempi di schemi di implementazione includono:

  • Un compilatore C che trasforma il codice macchina da C a x86 e una CPU x86 che esegue quel codice.
  • Un compilatore C che trasforma C in LLVM IR, un compilatore backend LLVM che trasforma LLVM IR in codice macchina x86 e una CPU x86 che esegue quel codice.
  • Un compilatore C che trasforma C in LLVM IR e un interprete LLVM che esegue l'IR LLVM.
  • Un compilatore Java che trasforma da Java a JVM bytecode e un JRE con un interprete che esegue quel codice.
  • Un compilatore Java che trasforma da Java a JVM bytecode e un JRE con un interprete che esegue alcune parti di quel codice e un compilatore che trasforma altre parti di quel codice in codice macchina x86 e una CPU x86 che esegue quel codice .
  • Un compilatore Java che trasforma da Java a JVM bytecode e una CPU ARM che esegue quel codice.
  • Un compilatore C # che trasforma C # in CIL, un CLR con un compilatore che trasforma CIL in codice macchina x86 e una CPU x86 che esegue quel codice.
  • Un interprete Ruby che esegue Ruby.
  • Un ambiente Ruby con un interprete che esegue Ruby e un compilatore che trasforma Ruby in codice macchina x86 e una CPU x86 che esegue quel codice.

... e così via.

    
risposta data 23.10.2015 - 14:04
fonte
7

Mentre le linee tra compilatori e interpreti sono diventate confuse nel tempo, si può ancora tracciare una linea tra di esse guardando la semantica di ciò che il programma dovrebbe fare e ciò che fa il compilatore / interprete.

Un compilatore genererà un altro programma (di solito in un linguaggio di livello inferiore come il codice macchina) che, se viene eseguito, farà ciò che il programma dovrebbe fare.

Un interprete farà ciò che il tuo programma dovrebbe fare.

Con queste definizioni, i punti in cui diventa sfocato sono i casi in cui si può pensare al compilatore / interprete come a fare cose diverse a seconda di come lo si guarda. Ad esempio, Python prende il tuo codice Python e lo compila in un bytecode Python compilato. Se questo bytecode Python viene eseguito tramite un interprete di bytecode Python , fa ciò che il programma avrebbe dovuto fare. Nella maggior parte delle situazioni, tuttavia, gli sviluppatori Python pensano che entrambi i passaggi vengano fatti in un unico grande passo, quindi scelgono di pensare a CPython interpreta come interpretando il loro codice sorgente, e il fatto che abbia compilato lungo la strada è considerato un dettaglio di implementazione. In questo modo, è tutta una questione di prospettiva.

    
risposta data 23.10.2015 - 01:50
fonte
6

Ecco una semplice disambiguazione concettuale tra compilatori e interpreti.

Considera 3 lingue: programmazione , P (in cosa è scritto il programma); lingua dominio , D (per ciò che accade con il programma in esecuzione); e la lingua target , T (qualche terza lingua).

Concettualmente,

  • un compilatore traduce P a T in modo da poter valutare T (D); considerando

  • un interprete valuta direttamente P (D).

risposta data 22.10.2015 - 22:09
fonte

Leggi altre domande sui tag