インタープリターはマシンコードを生成しますか?


42

コンパイラーとインタープリターのトピックを集中的に研究しています。基本的な理解が正しいかどうかを確認したいので、次のことを想定しましょう。

「Foobish」という言語があり、そのキーワードは

<OUTPUT> 'TEXT', <Number_of_Repeats>;

したがって、コンソールに10回印刷する場合は、次のように記述します。

OUTPUT 'Hello World', 10;

こんにちはWorld.foobish-file。

次に、選択した言語(この場合はC#)でインタープリターを作成します。

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

非常に簡単なインタープリターレベルでは、インタープリターはスクリプトファイルなどを分析し、インタープリターの実装の方法でfoobish-languageを実行します。

コンパイラは、物理ハードウェア上で直接実行される機械語を作成しますか?

インタプリタは機械語を生成しませんが、コンパイラは入力のために機械語を生成しますか?

コンパイラとインタプリタの基本的な動作方法に誤解はありますか?


21
C#の「コンパイラ」は何をしていると思いますか?ヒントとして、マシンコードを生成しません
フィリップケンドール

3
Javaコンパイラは、JVMのコードを生成します。そのため、コンパイラのターゲットマシンは、ハードウェアによって直接実行されない仮想マシンにすることができます。インタプリタとコンパイラの主な違いは、コンパイラが最初にソースコード全体をチェックし、ターゲットマシン言語に翻訳することです。このコンパイルされたコードは、対象のマシンによって実行されます。一方、インタープリターはプログラムのチャンクをその場で翻訳して実行します。
ジョルジョ

@Giorgio:つまり、JITのようなものですか?
ロバートハーヴェイ

2
@RobertHarvey:私はJavaコンパイラ(javac)を意味しました:私の知る限り、JVMのバイトコードを生成します。そして再び、私は後で(実行時に)JITがバイトコードをコンパイルします。これは非常に頻繁に使用され、ネイティブの機械語になります。
ジョルジョ

4
コンパイラは翻訳を意味します。c、アセンブリ、javascript、マシンコードなど、あらゆる種類の言語を出力できます。
エスベンスコフペダーセン

回答:


77

「インタープリター」および「コンパイラー」という用語は、以前よりもはるかに曖昧です。何年も前、コンパイラが後で実行されるマシンコードを生成するのがより一般的でしたが、インタプリタは多少なりともソースコードを直接「実行」しました。そのため、これらの2つの用語は当時よく理解されていました。

しかし今日、「コンパイラ」と「インタープリター」の使用には多くのバリエーションがあります。たとえば、VB6はバイトコード(中間言語の形式)に「コンパイル」され、VBランタイムによって「解釈」されます。同様のプロセスがC#で行われます。C#はCILを生成し、それがジャストインタイムコンパイラ(JIT)によって実行されます。JITは、昔はインタープリターと考えられていました。NGen.exeを使用して、JITの出力を実際のバイナリ実行可能ファイルに「フリーズドライ」できます。NGen.exeの製品は、昔はコンパイラの結果でした。

したがって、あなたの質問に対する答えは、かつてほど簡単ではありません。

Wikipediaでのさらに読む
コンパイラと通訳


6
@Giorgio:最近のほとんどのインタープリターは、実際にはソースコードを実行せず、むしろASTまたは同様のものの出力を実行します。コンパイラにも同様のプロセスがあります。区別は、あなたが思うほど明確ではありません。
ロバートハーベイ

5
「JITの出力をNGen.exeを使用して実際のバイナリ実行可能ファイルに「フリーズドライ」することができます。その製品は昔はコンパイラの結果でした。」しかし、今日でもその結果ですコンパイラ(つまり、ジャストインタイムコンパイラ)の。コンパイラがいつ実行されるかは関係ありませんが、何が行われますか。コンパイラーは、入力としてコードの表現を受け取り、新しい表現を出力します。インタープリターは、そのコードを実行した結果を出力します。これらは、どのように混合し、いつ実行するかに関係なく、2つの異なるプロセスです。
ジョルジオ

4
「コンパイラ」は、単にGCCに添付するために選択した用語です。彼らは、マシンコードを生成する場合でもコンパイラーを呼び出さないことを選択し、代わりにその用語を前のステップに添付することを好みました。私の要点は、今日では「コンパイラーまたはインタープリター」と呼ばれるものを決定的に呼び出すために呼び出すことができるバインディング原則はないということです。
ロバートハーベイ

4
私の非常に限られた理解にあるように、最近ではx86 CPUはハードウェアベースのJITエンジンの半分に過ぎず、アセンブリは正確に実行されるものと常に関係を失っています。
ルーシェンコ

4
@RobertHarveyインタープリターとコンパイラーで使用される技術の間に明確な境界線がないことに同意する一方で、機能にはかなり明確な区分があります:入力としてプログラムのコードで特定のツールを実行した結果がその実行である場合プログラム、ツールはインタプリタです。結果が、プログラムをより抽象度の低い形式に変換した結果である場合、それはコンパイラーです。結果がより抽象的な形式への変換の場合、逆コンパイラの場合。ただし、これらの結果が複数ある場合はあいまいです。
ジュール

34

以下の要約は、「コンパイラ、原則、テクニック、およびツール」、Aho、Lam、Sethi、Ullman(Pearson International Edition、2007)、ページ1、2、および独自のアイデアに基づいています。

プログラムを処理するための2つの基本的なメカニズムは、コンパイル解釈です。

コンパイルは、入力として特定の言語のソースプログラムを受け取り、ターゲット言語でターゲットプログラムを出力します。

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

ターゲット言語がマシンコードの場合、いくつかのプロセッサーで直接実行できます。

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

コンパイルには、入力プログラム(またはモジュール)全体のスキャンと翻訳が含まれ、実行は含まれません。

解釈は、入力としてソースプログラムとその入力を受け取り、ソースプログラムの出力を生成します

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

解釈には通常、プログラムを一度に1ステートメントずつ処理(分析および実行)することが含まれます。

実際には、多くの言語プロセッサは2つのアプローチを組み合わせて使用​​しています。たとえば、Javaプログラムは最初に中間プログラム(バイトコード)に翻訳(コンパイル)されます。

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

このステップの出力は、仮想マシンによって実行(解釈)されます:

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

さらに複雑にするために、JVMは実行時にジャストインタイムコンパイルを実行してバイトコードを別の形式に変換し、それを実行できます。

また、機械語にコンパイルする場合でも、基礎となるプロセッサによって実装されるバイナリファイルを実行するインタプリタがあります。したがって、この場合でも、コンパイルと解釈のハイブリッドを使用しています。

したがって、実際のシステムではこの2つの組み合わせが使用されるため、特定の言語プロセッサがコンパイラかインタープリターかを判断するのは困難です。おそらく、処理のさまざまな段階で両方のメカニズムを使用するからです。この場合、別のより中立的な用語を使用する方がおそらく適切です。

それでも、上記の図で説明したように、コンパイルと解釈は2つの異なる種類の処理です。

最初の質問に答えるため。

コンパイラは、物理ハードウェア上で直接実行される機械語を作成しますか?

必ずしも、コンパイラーはマシンM1用に作成されたプログラムを、マシンM2用に作成された同等のプログラムに変換します。ターゲットマシンは、ハードウェアに実装することも、仮想マシンにすることもできます。概念的には違いはありません。重要な点は、コンパイラーがコードの一部を見て、実行せずに別の言語に翻訳することです。

では、インタープリターは機械語を生成しませんが、コンパイラーは入力のために機械語を生成しますか?

生成することで出力を参照している場合、コンパイラーは機械語のターゲットプログラムを生成ますが、インタープリターは生成しません。


7
言い換えると、インタープリターはプログラムPを取得してその出力Oを生成し、コンパイラーはPを取得してOを出力するプログラムP 'を生成します。インタプリタには、コンパイラ(バイトコード、中間表現、JITマシン命令など)であるコンポーネントが含まれることがよくあり、同様にコンパイラ(たとえば、コンパイル時の計算を評価するためのインタプリタ)が含まれる場合があります。
ジョンパーディ

「コンパイラにインタープリターが含まれる場合があります(たとえば、コンパイル時の計算を評価するため)」:良い点。LispマクロとC ++テンプレートは、この方法で前処理されると思います。
ジョルジョ

さらに簡単に、CプリプロセッサはCPPディレクティブを使用してCソースコードをプレーンCにコンパイルし、などのブール式のインタープリターを組み込みdefined A && !defined Bます。
ジョンパーディ

@JonPurdy私もそれに同意しますが、ソースのおそらくトークン化されたバージョンを超えて中間表現を使用しない「伝統的なインタープリター」クラスを追加します。例としては、シェル、多くのBASIC、クラシックLisp、8.0より前のTcl、bcなどがあります。
ホッブズ

1
@naxa-コンパイラのタイプに関するLawrenceの回答とPaul Draperのコメントを参照してください。アセンブラは、(1)出力言語がマシンまたは仮想マシンによる直接実行を目的とし、(2)入力ステートメントと出力命令の間に非常に単純な1対1の対応がある特別な種類のコンパイラです。
ジュール

22

コンパイラは機械語を作成します

いいえ。コンパイラは、言語Aで記述されたプログラムを入力として受け取り、言語Bで意味的に同等のプログラムを出力として生成する単なるプログラムです。言語Bは何でもかまいませんが、機械語である必要はありません。

コンパイラーは、高水準言語から別の高水準言語(JavaをECMAScriptにコンパイルするGWTなど)、高水準言語から低水準言語(たとえば、SchemeをCにコンパイルするGambit)にコンパイルできます。高レベル言語からマシンコード(JavaをネイティブコードにコンパイルするGCJなど)、低レベル言語から高レベル言語(CをJava、Lua、Perl、ECMAScript、CommonにコンパイルするClueなど) Lisp)、低レベル言語から別の低レベル言語(JVMLバイトコードをDalvikバイトコードにコンパイルするAndroid SDKなど)、低レベル言語からマシンコード(HotSpotの一部であるC1Xコンパイラなど) JVMLバイトコードをマシンコードにコンパイルします)、マシンコードを高水準言語(いわゆる「逆コンパイラ」、LLVMマシンコードをECMAScriptにコンパイルするEmscripten)に変換します。低レベル言語へのマシンコード(x86ネイティブコードをJVMLバイトコードにコンパイルするJPCのJITコンパイラー)およびネイティブコードからネイティブコード(たとえば、PowerPCネイティブコードをx86ネイティブコードにコンパイルするPearPCのJITコンパイラー)

また、「マシンコード」はいくつかの理由で本当にあいまいな用語であることに注意してください。たとえば、JVMバイトコードをネイティブに実行するCPUや、x86マシンコード用のソフトウェアインタープリターがあります。それでは、1つを「ネイティブマシンコード」にし、もう1つを作成しないのはなぜでしょうか。また、すべての言語は、その言語の抽象マシンのコードです。

特別な機能を実行するコンパイラには多くの専門名があります。これらは特別な名前であるという事実にもかかわらず、これらはすべてコンパイラであり、特別な種類のコンパイラです。

  • 言語Aが言語Bとほぼ同じ抽象レベルにあると認識されている場合、コンパイラはトランスパイラーと呼ばれる場合があります(例:Ruby-to-ECMAScript-transpilerまたはECMAScript2015-to-ECMAScript5-transpiler)
  • 言語Aが言語Bよりも抽象化レベルが低いと認識されている場合、コンパイラーはコンパイラー(たとえば、x86-machine-code-to-C-decompiler)と呼ばれることがあります
  • 言語A ==言語Bの場合、コンパイラーはオプティマイザー難読化ツール、またはミニファイアー(コンパイラーの特定の機能に応じて)と呼ばれる場合があります

物理ハードウェア上で直接実行されるのはどれですか?

必ずしも。インタープリターまたはVMで実行できます。さらに別の言語にコンパイルできます。

では、インタープリターは機械語を生成しませんが、コンパイラーは入力のために機械語を生成しますか?

インタプリタは何も生成しません。プログラムを実行するだけです。

コンパイラは何かを生成しますが、必ずしも機械語である必要はなく、どの言語でもかまいません。入力言語と同じ言語でもかまいません!たとえば、Supercompilers、LLCには、入力としてJavaを受け取り、出力として最適化されたJavaを生成するコンパイラがあります。ECMAScriptを入力として受け取り、最適化、縮小、および難読化されたECMAScriptを出力として生成する多くのECMAScriptコンパイラがあります。


以下にも興味があるかもしれません:


16

「コンパイラーインタープリター」という概念を完全に捨てるべきだと思います。それは誤った二分法だからです。

  • コンパイラがある変圧器:それは書かれたコンピュータプログラム変換ソース言語のとで同等の出力ターゲット言語を。通常、ソース言語はターゲット言語よりも高レベルです。逆の場合は、この種のトランスフォーマーを逆コンパイラーと呼ぶことがよくあります。
  • インタプリタは、ある実行エンジン。その言語の仕様に従って、1つの言語で書かれたコンピュータープログラムを実行します。ほとんどの場合、ソフトウェアという用語を使用します(ただし、ある意味では、従来のCPUはマシンコードのハードウェアベースの「インタープリター」と見なすことができます)。

現実の世界で抽象プログラミング言語を有用にするための集合的な言葉は実装です。

過去において、プログラミング言語の実装は、多くの場合、コンパイラ(およびコードを生成したCPU)またはインタプリタのみで構成されていたため、これら2種類のツールは相互に排他的であるように見えました。今日、これはそうではないことがはっきりとわかります(そして、それは最初からではありませんでした)。洗練されたプログラミング言語の実装を採用し、「コンパイラ」または「インタープリター」という名前を付けようとすると、多くの場合、結果が不確定または一貫性のないものになります。

単一のプログラミング言語の実装には、多くの場合複数の形式(スタンドアロン、オンザフライ)の任意の数のコンパイラーとインタープリター静的アナライザーオプティマイザーなどの任意の数のツール、および任意の数のステップを含めることができます。任意の数の中間言語の実装全体を含めることもできます(これは実装されている言語とは無関係かもしれません)。

実装スキームの例は次のとおりです。

  • Cをx86マシンコードに変換するACコンパイラ、およびそのコードを実行するx86 CPU。
  • CをLLVM IRに変換するACコンパイラ、LLVM IRをx86マシンコードに変換するLLVMバックエンドコンパイラ、およびそのコードを実行するx86 CPU。
  • CをLLVM IRに変換するACコンパイラー、およびLLVM IRを実行するLLVMインタープリター。
  • JavaをJVMバイトコードに変換するJavaコンパイラと、そのコードを実行するインタープリターを備えたJRE。
  • JavaをJVMバイトコードに変換するJavaコンパイラー、およびそのコードの一部を実行するインタープリターとそのコードの他の部分をx86マシンコードに変換するコンパイラー、およびそのコードを実行するx86 CPUの両方を備えたJRE。
  • JavaをJVMバイトコードに変換するJavaコンパイラと、そのコードを実行するARM CPU。
  • C#をCILに変換するC#コンパイラ、CILをx86マシンコードに変換するコンパイラを備えたCLR、およびそのコードを実行するx86 CPU。
  • Rubyを実行するRubyインタープリター。
  • Rubyを実行するインタープリターとRubyをx86マシンコードに変換するコンパイラーと、そのコードを実行するx86 CPUの両方を備えたRuby環境。

...等々。


中間表現用に設計されたエンコード(例:Javaバイトコード)でさえハードウェア実装が可能であることを指摘して+1。
ジュール

7

コンパイラとインタプリタの間の線は時間の経過とともに曖昧になりましたが、プログラムが何をすべきか、コンパイラ/インタプリタが何をするかの意味論を見ることで、それらの間に線を引くことができます。

コンパイラーは別のプログラムを生成し(通常、マシンコードのような低レベル言語で)、そのプログラムが実行されると、プログラムが行うべきことを行います。

通訳者はあなたのプログラムがすべきことをします。

これらの定義では、ファジーになる場所は、コンパイラー/インタープリターが見方によって異なることをしていると考えることができる場合です。例えば、PythonはあなたのPythonコードを受け取り、コンパイルコンパイル済みのPythonバイトコードにそれを。このPythonバイトコードがPythonバイトコードインタープリターを介して実行される場合、プログラムが実行するはずの処理が実行されます。ただし、ほとんどの場合、Python開発者はこれらの両方のステップを1つの大きなステップで行うと考えているため、CPythonインタープリターをソースコードの解釈と考えることを選択し、途中でコンパイルされたという事実は実装の詳細と見なされます。このように、それはすべて視点の問題です。


5

コンパイラとインタープリターの間の簡単な概念的曖昧性解消を次に示します。

3つの言語を検討してください:プログラミング言語、P(プログラムの記述内容); ドメイン言語、D(実行中のプログラムで何が起こるか); そして、ターゲット言語、T(いくつかの第三言語)。

概念的には、

  • T(D)を評価できるように、コンパイラはPをTに変換します。一方、

  • インタプリタは直接P(D)を評価します。


1
最新のインタープリターのほとんどは、実際にソース言語を直接評価するのではなく、ソース言語の中間表現を評価します。
ロバートハーベイ

4
@RobertHarveyそれは用語間の概念的な違いを変えません。
ローレンス

1
したがって、インタープリターとして実際に参照しているのは、中間表現を評価する部分です。中間表現を作成する部分は、定義によりコンパイラです。
ロバートハーヴェイ

6
@RobertHarveyそうでもない。用語は、作業している抽象化のレベルに依存します。下を見ると、ツールは何でもできます。たとえば、外国に行ってバイリンガルの友人であるボブを連れてきたとしましょう。あなたがボブと話をして地元の人と話をし、ボブが次に地元の人と話をすると、ボブはあなたと通訳をします(たとえ話をする前に彼が彼らの言語で落書きをしても)。Bobにフレーズを要求し、Bobが外国語でそれらを作成し、それらの記述(Bobではない)を参照してローカルと通信する場合、Bobはコンパイラーとして機能します。
ローレンス

1
素晴らしい答え。注目に値する:最近では、「トランスパイラー」が聞こえます。それは、PとTが類似の抽象化レベルであるコンパイラーです。(たとえば、ES5からES6へのトランスパイラー。)
ポールドレイパー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.