プログラミング言語のどのプロパティがコンパイルを不可能にしますか?


72

質問:

「プログラミング言語の特定のプロパティでは、コードに記述されたコードを実行するための唯一の方法は解釈によるものです。つまり、従来のCPUのネイティブマシンコードへのコンパイルは不可能です。これらのプロパティは何ですか?」

コンパイラ:Parag H. DaveおよびHimanshu B. Daveによる原則と実践(2012年5月2日)

本は答えについての手がかりを与えません。プログラミング言語の概念(SEBESTA)で答えを見つけようとしましたが、役に立ちませんでした。ウェブ検索もあまり役に立ちませんでした。手がかりはありますか?


31
有名なのは、Perlを解析することすらできないことです。それ以外は、さらなる仮定なしに主張は些細な間違いのようです:インタプリタがあれば、いつでもインタプリタとコードを1つの実行可能ファイルにまとめることができます。
ラファエル

4
@Raphael:いいアイデアですが、... 1)実行される前にコードが利用可能であると仮定しています。インタラクティブな使用には当てはまりません。確かに、bashステートメントまたはPostScriptスタックコンテンツのネイティブコードにジャストインタイムコンパイルを使用できますが、それはかなりおかしいアイデアです。2)あなたのアイデアは実際にコードをコンパイルしません:バンドルはコードのコンパイルされたバージョンではなく、コードのインタープリターです。
reinierpost 14

7
古き良き時代には、gwbasicプログラムを自己編集していました(gwbasicは基本プログラムをバイトコードの一種で保存します)。私は現在、それらを編集する能力を保持しながらネイティブマシンコードにコンパイルするための健全な方法を考えることができません。
PlasmaHH 14

15
@PlasmaHH:自己修正コードは1948年に遡ります。最初のコンパイラは1952年に記述されました。自己修正コードの概念はネイティブマシンコードで発明されました。
Mooingダック14

10
@reinierpost Raphaelはこの問題について理論的な立場を取っています。質問の概念的な制限を示すメリットがあります。コンパイルは、言語Sから言語Tへの翻訳です。言語Tは、他の言語の解釈コードを追加できるSの拡張である可能性があります。したがって、Sとそのインタープリターを束ねるのは言語Tのプログラムです。エンジニアにとっては馬鹿げているように見えますが、質問を有意義に定式化することは容易ではないことを示しています。エンジニアリングの観点から、許容可能なコンパイルプロセス(ラファエルなど)と許容できないコンパイルプロセスをどのように区別しますか?
babou 14

回答:


61

解釈されたコードとコンパイルされたコードの違いは、おそらくラフィエルのコメントが強調するフィクションです。

the claim seems to be trivially wrong without further assumptions: if there is
an interpreter, I can always bundle interpreter and code in one executable ...

事実は、コードは常にソフトウェア、ハードウェア、または両方の組み合わせによって解釈され、コンパイルプロセスはどちらになるかを判断できないということです。

コンパイルとみなすのは、ある言語(ソース)から別の言語(ターゲット)への翻訳プロセスです。また、のインタープリターは通常のインタープリターとは異なります。STST

コンパイルされたプログラムは、ある構文形式から別の構文形式に変換され、言語およびの意図されたセマンティクスが与えられると、およびは同じ計算動作を持ち、通常はいくつかのことを試みます複雑さや単純な効率(時間、スペース、表面、エネルギー消費)など、おそらく最適化のために変更します。正確な定義が必要になるため、機能的同等性については話さないようにしています。PSPTSTPSPT

実際、一部のコンパイラは、実行を「改善」するのではなく、単にコードのサイズを縮小するために使用されています。これは、プラトンシステムで使用されている言語の場合です(ただし、コンパイルとは呼ばれていません)。

コンパイルプロセス後にのインタープリターが不要になった場合、コードが完全にコンパイルされたと見なすことができます。少なくとも、それは理論的な質問ではなく工学的な質問としてあなたの質問を読むことができる唯一の方法です(理論的には、通訳をいつでも再構築できるからです)。S

問題を引き起こす可能性のあるものの1つ、afaikはメタ循環性です。これは、プログラムが独自のソース言語で構文構造を操作し、プログラムフラグメントを作成して、元のプログラムの一部であるかのように解釈する場合です。あなたが言語で任意のプログラムの断片を生成することができますので、無意味な構文フラグメントを操作する任意の計算の結果として、私はあなたが(エンジニアリングの観点から)、それはほぼ不可能にすることができ、言語にプログラムをコンパイルするために推測ように、フラグメントを生成するようになりました。したがって、のインタープリター、または少なくともからSSTTSSTで生成されたフラグメントをオンザフライでコンパイルするための(このドキュメントも参照)。S

しかし、これをどのように適切に定式化できるのかはわかりません(そして、今のところそれに時間がない)。そして、形式化されていない問題の大きな言葉は不可能です。

さらなる発言

36時間後に追加されました。この非常に長い続編をスキップすることもできます。

この質問に対する多くのコメントは、問題の2つの見解を示しています。それは無意味であると考える理論的見解と、残念ながらそれほど簡単に定式化されていない工学的見解です。

解釈とコンパイルを見る方法はたくさんありますが、いくつかをスケッチしてみます。できる限り非公式になろうとする

トゥームストーン図

初期の形式化(1960年代初期から1990年後半)の1つは、Tまたは Tombstone図です。これらの図は、構成可能なグラフィカル要素で、インタプリタまたはコンパイラの実装言語、解釈またはコンパイルされるソース言語、およびコンパイラの場合のターゲット言語を提示しました。より複雑なバージョンでは、属性を追加できます。これらのグラフィック表現は、公理、推論規則、カリー・ハワードのような公理からの存在の証明からプロセッサ生成を機械的に導出するために使用できる推論ルールとして見ることができます(60年代に行われたかはわかりませんが:)。

部分評価

別の興味深い見方は、部分評価のパラダイムです。私は、いくつかの入力データが与えられたときに答えを計算する一種の関数実装として、プログラムの単純な見方を取っています。次に、インタプリタ 言語のためのプログラム取るプログラムである に書き込まれた及びデータ、そのプログラムのためのセマンティクスに従って結果を計算。部分的な評価は二つの引数のプログラムを専門とする技術であると、唯一の引数、と言う、知られています。目的は、最終的に2番目の引数を取得したときに、より高速な評価を得ることです。ISSpSSdSa1a2a1a2。場合は特に便利ですより頻繁に変更と部分評価の費用として唯一のすべての計算法で償却することができます変化しています。a2a1a1a2

これは、アルゴリズムの設計でよくある状況です(多くの場合、SE-CSに関する最初のコメントのトピック)。データのより静的な部分が前処理されるため、すべてのアプリケーションで前処理のコストを償却できます。入力データのより多くの可変部分を持つアルゴリズムの。

最初の引数は実行されるプログラムであり、通常は異なるデータを使用して何度も実行される(または異なるデータを使用してサブパートを複数回実行する)ため、これもインタープリターの状況です。したがって、最初の引数としてこのプログラムで部分的に評価することにより、特定のプログラムのより高速な評価のためにインタープリターを特化することは自然な考えになります。これは、プログラムをコンパイルする方法と見なされる場合があり、その最初の(プログラム)引数でのインタープリターの部分評価によるコンパイルに関する重要な研究作業があります。

Smn定理

部分評価アプローチの良い点は、特にKleeneのSmn定理において、理論に根ざしているということです(理論は嘘つきになりますが) 。ここでは、純粋な理論家を混乱させないことを期待して、直感的なプレゼンテーションをしようとしています。

ゲーデル数を考える再帰関数の、あなたが見ることができますゲーデル数を与えられたように、ハードウェアとして (読み取りオブジェクトコードをプログラムの)で定義された関数である(すなわち上のオブジェクトコードによって計算しますハードウェア)。φφpφpp

最も単純な形式では、定理は次のようにウィキペディアに記載されています(表記法の小さな変更まで)。

ゲーデル数の所与再帰関数の、原始再帰関数が存在する次のプロパティを持つ2つの引数は:すべてのゲーデル数のための部分計算関数二つの引数、式ととは、自然数と同じ組み合わせに対して定義され、それらの値はそのような組み合わせに対して等しくなります。換言すれば、関数の次伸長平等は、すべてに対して成り立つ: φσqfφσ(q,x)(y)f(x,y)xyxφσ(q,x)λy.φq(x,y).

今、撮影インタープリタとして、プログラムのソースコードとして、およびデータとしてそのプログラムのため、我々は書くことができる: qISxpSydφσ(IS,pS)λd.φIS(pS,d).

φISは 、ハードウェア上でのインタープリター実行、つまり言語書かれたプログラムを解釈する準備が整ったブラックボックスとして見ることができます。ISS

機能インタプリタを専門関数として見られるかもしれないプログラムの部分評価のように、。したがって、ゲーデル数は、プログラムコンパイル済みバージョンであるオブジェクトコードが含まれています。σISPSσ(IS,pS)pS

したがって、関数は 、言語で記述されたプログラムソースコードを引数として取り、そのプログラムのオブジェクトコードバージョンを返す関数とことができます。。したがって、は通常コンパイラと呼ばれます。CS=λqS.σ((IS,qS)qSSCS

いくつかの結論

しかし、私が言ったように、「理論は嘘つきになりえます」、または実際には1つに思えます。問題は、関数について何も知らないことです。実際にはそのような関数が多くあり、定理の証明は非常に単純な定義を使用する可能性がありますが、エンジニアリングの観点からは、Raphaelによって提案されたソリューションよりも優れている可能性があります:ソースコードインタプリタと。これはいつでも実行できるため、コンパイルは常に可能です。Q S I SσqSIS

コンパイラとは何かというより制限的な概念を形式化するには、より微妙な理論的アプローチが必要になります。私はその方向で何が行われたのか分かりません。部分評価で行われる非常に現実的な作業は、エンジニアリングの観点からより現実的です。そしてもちろん、カリー・ハワード同型に基づいて型理論の文脈で開発された、仕様の証明からプログラムを抽出するなど、コンパイラを書くための他のテクニックがあります。

ここでの私の目的は、Raphaelの発言が「クレイジー」ではないことを示すことですが、物事が明白ではなく、単純でさえないことを正気に思い起こさせることです。何かが不可能であると言うことは、それが不可能である理由と理由を正確に理解するためだけに、正確な定義と証明を必要とする強力な声明です。しかし、そのような証拠を表現するために適切な形式化を構築することは非常に難しいかもしれません。

これは、特定の機能がコンパイル可能でない場合でも、エンジニアが理解している意味で、標準のコンパイル手法は、そのような機能を使用しないプログラムの部分に常に適用できることです。

Gillesの重要な発言をフォローするには、言語によっては、コンパイル時に実行されるものと実行時に実行される必要があるものがあるため、特定のコードが必要であるため、コンパイルの概念は実際にあることがわかります。不明確であり、おそらく満足のいく方法で定義できない。コンパイルは最適化プロセスにすぎません。一部のアルゴリズムで静的データの前処理と比較したときに、部分評価のセクションで示しました。

複雑な最適化プロセスとして、コンパイルの概念は実際には連続体に属します。言語またはプログラムの特性に応じて、一部の情報が静的に利用でき、最適化が改善される場合があります。その他のものは、実行時に延期する必要があります。物事が本当に悪くなったときは、少なくともプログラムの一部について実行時にすべてを実行する必要があります。ソースコードをインタープリターにバンドルするだけで十分です。したがって、このバンドルは、このコンパイルの連続体のローエンドにすぎません。コンパイラに関する研究の多くは、以前は動的に行われていたことを静的に行う方法を見つけることに関するものです。コンパイル時のガベージコレクションは良い例のようです。

コンパイルプロセスでマシンコードを生成する必要があると言っても意味がないことに注意してください。インタープリターはマシンコードであるため、これがまさにバンドルです。


3
不可能は大きな言葉」非常大きな言葉。=)
ブライアンS 14

3
実行中のプログラムが最初の入力を受け取る前に完全行われる一連のステップを指す「コンパイル」を定義し、プログラムの抽象機械モデルの一部ではない手段を介したデータ制御プログラムフローのプロセスであると解釈する場合、言語をコンパイルするためには、コンパイラーが、実行が始まる前に、言語コンストラクトが持つ可能性のあるあらゆる意味を識別できる必要があります。言語コンストラクトに無制限の意味が含まれる可能性がある言語では、コンパイルは機能しません。
supercat

@BrianSいいえ、そうではなく、そうでないことを証明することは不可能です;)
マイケルガゾンダ14

@supercatまだ定義ではありません。言語構成体の「意味」とは何ですか?
ライモイド14

コンパイラ/インタープリターをある種の部分的な実行と見なす概念が大好きです!
ベルギ14

17

問題は、実際にはコンパイルが不可能であることではありません。言語を解釈できる場合¹、インタプリタにソースコードをバンドルすることにより、簡単な方法でコンパイルできます。問題は、どの言語機能がこれを本質的に唯一の方法にするかを尋ねることです。

インタープリターは、ソースコードを入力として受け取り、そのソースコードのセマンティクスで指定されたとおりに動作するプログラムです。インタープリターが必要な場合、これは言語にソースコードを解釈する方法が含まれていることを意味します。この機能はと呼ばれevalます。言語のランタイム環境の一部としてインタープリターが必要な場合、その言語には以下が含まれることを意味しますevalevalプリミティブとして存在するか、何らかの方法でエンコードすることができます。通常、スクリプト言語として知られる言語には、evalほとんどのLisp方言と同様に機能が含まれています。

言語に含まれevalているからといって、その大部分をネイティブコードにコンパイルできないわけではありません。たとえば、最適なLispコンパイラがあり、優れたネイティブコードを生成し、それでもサポートしていevalます。evalされたコードは解釈されるか、その場でコンパイルされます。

eval究極のインタープリター機能が必要ですが、インタープリターに足りない何かを必要とする他の機能があります。コンパイラーのいくつかの典型的なフェーズを検討してください。

  1. 解析
  2. 型チェック
  3. コード生成
  4. リンク

evalつまり、これらのフェーズはすべて実行時に実行する必要があります。ネイティブコンパイルを困難にする他の機能があります。下から見て、一部の言語は、関数(メソッド、プロシージャなど)と変数(オブジェクト、参照など)が非ローカルコードの変更に依存する方法を提供することにより、レイトリンクを奨励しています。これにより、効率的なネイティブコードを生成することが困難になります(不可能ではありません)。オブジェクト参照を仮想マシンの呼び出しとして保持し、VMエンジンがバインディングをオンザフライで処理しやすくなります。

一般的に、リフレクションは言語をネイティブコードにコンパイルするのを難しくする傾向があります。評価プリミティブは、反射の極端なケースです。多くの言語は、そこまで行くが、それにもかかわらず、仮想マシンの面で定義された意味を持っている、サンプルコードは、名前でクラスを取得し、その継承を検査し、そのメソッドをリスト、メソッドを呼び出すなどしを可能にしていないのJavaJVM .NETを使用したC#と2つの有名な例があります。これらの言語を実装する最も簡単な方法は、それらをbytecodeにコンパイルすることですが、それにもかかわらず、少なくとも高度なリフレクション機能を使用しないプログラムフラグメントをコンパイルするネイティブコンパイラ(多くのジャストインタイム)があります。

型チェックは、プログラムが有効かどうかを判断します。言語によって、コンパイル時と実行時の分析の実行基準が異なります。言語は、コードの実行を開始する前に多くのチェックを実行する場合は「静的型付け」と呼ばれ、実行しない場合は「動的型付け」と呼ばれます。一部の言語には、動的キャスト機能または非整列化と型チェック機能が含まれています。これらの機能を使用するには、ランタイム環境にタイプチェッカーを埋め込む必要があります。これは、ランタイム環境にコードジェネレーターまたはインタープリターを含める要件に直交しています。

¹ 演習:解釈できない言語を定義します。


(1)ソースコードをコンパイルとしてカウントするインタープリターをバンドルすることに同意しませんが、あなたの投稿の残りは素晴らしいです。(2)評価について完全に同意する。(3)なぜリフレクションが言語をネイティブコードにコンパイルするのを難しくするのか分かりません。Objective-Cにはリフレクションがあり、(おそらく)コンパイルされます。(4)あいまいに関連するメモ、C ++テンプレートメタマジックは通常、コンパイルされて実行されるのではなく解釈されます。
Mooingダック14

偶然、Luaがコンパイルされました。はeval単にバイトコードをコンパイルし、その後、別のステップとしてバイナリがバイトコードを実行します。そして、コンパイルされたバイナリに間違いなく反映されています。
Mooingダック14

ハーバードアーキテクチャマシンでは、コンパイルにより「データ」としてアクセスする必要のないコードが生成されます。コードが実際に「コンパイル」されているのではなく、データとして保存する必要があるソースファイルからの情報を推測します。コンパイラint arr[] = {1,2,5};が[1,2,5]を含む初期化データセクションのような宣言を生成して生成することには何の問題もありませんが、[1,2,5]をマシンコードに変換する動作については説明しません。プログラムのほぼすべてをデータとして保存する必要がある場合、プログラムのどの部分が実際に「コンパイル」されますか?
supercat

2
@supercatこれは、数学者とコンピューター科学者が意味することです。それは数学的定義に適合しますが、興味深いことは何も起こりません。
ジル14

@Gilles:「compile」という用語が(関連する「データ」を保持せずに)マシン命令への翻訳用に予約されており、コンパイル言語でそれを受け入れる場合、配列宣言の動作は配列を「コンパイル」しません。コードの意味のある部分をコンパイルすることが不可能な言語がいくつかあります。
supercat 14

13

著者は、コンパイルが意味すると仮定していると思います

  • ソースプログラムは実行時に存在する必要はありません。
  • 実行時にコンパイラーまたはインタープリターが存在する必要はありません。

そのようなスキームに対して「不可能」ではないとしても問題を引き起こすいくつかのサンプル機能を以下に示します。

  1. 名前(文字列)で変数を参照することにより、実行時に変数の値を調べることができる場合、実行時に変数名が必要です。

  2. 名前(文字列)で参照することにより、実行時に関数/プロシージャを呼び出すことができる場合、実行時に関数/プロシージャ名が必要になります。

  3. たとえば、別のプログラムを実行したり、ネットワーク接続などから読み込んだりして、実行時にプログラムを(文字列として)構築できる場合は、実行時にインタープリターまたはコンパイラが必要です。このプログラムを実行します。

Lispには3つの機能すべてがあります。そのため、Lispシステムには実行時に常にインタープリターがロードされます。言語JavaおよびC#には、実行時に使用可能な関数名と、それらの意味を調べるためのテーブルがあります。おそらくBasicやPythonのような言語も、実行時に変数名を持っています。(私はそれについて100%確信していません)。


「インタープリター」がコードにコンパイルされるとどうなりますか?たとえば、ディスパッチテーブルを使用して仮想メソッドを呼び出す場合、これらは解釈またはコンパイルの例ですか?
アーウィンボルウィット14

2
「実行時にコンパイラやインタプリタが存在する必要はありません」それが本当なら、深い意味で、ほとんどのプラットフォームでCを「コンパイル」することはできません。Cランタイムは、起動、スタックのセットアップなど、atexit処理のためのシャットダウンなど、あまり実行することはありません。しかし、まだそこにある必要があります。
仮名

1
「Lispシステムでは、実行時に常にインタープリターがロードされます。」- 必ずしも。多くのLispシステムには、実行時にコンパイラがあります。通訳者さえいない人もいます。
ヨルグWミットタグ14

2
良い試みですが、en.wikipedia.org/wiki/Lisp_machine#Technical_overview。Lispをコンパイルし、結果を効率的に実行するように設計されています。
ピーター-モニカを復活させる14年

@Pseudonym:Cランタイムはライブラリであり、コンパイラでもインタープリターでもありません。
Mooingダック14

8

現在の返信がステートメント/回答を「考え直している」可能性があります。著者が言及しているのは、おそらく次の現象です。多くの言語には「評価」のようなコマンドがあります。たとえば、javascript evalを参照してください。その動作は、CS理論の特別な部分として一般的に研究されています(たとえば、Lisp)。このコマンドの機能は、言語定義のコンテキストで文字列を評価することです。したがって、実際には「組み込みコンパイラ」と類似しています。コンパイラーは、実行時まで文字列の内容を知ることができません。したがって、コンパイル時に評価結果をマシンコードにコンパイルすることはできません。

他の回答は、解釈された言語とコンパイルされた言語の区別は、多くの場合、「ジャストインタイムコンパイラ」または別名「ホットスポット」(たとえば、V8などの JavaScriptエンジンがますます同じテクニックを使用するJavaなど)のようなより現代の言語で著しく曖昧になる可能性があることを指摘しています。「評価のような」機能は確かにそれらの1つです。


2
V8は良い例です。これは純粋なコンパイラであり、解釈は行われません。それでも、無制限を含むECMAScriptの完全なセマンティクスを引き続きサポートしていますeval
ヨルグWミットタグ14

1
Luaも同じことをします。
Mooingダック14

3

LISPは恐ろしい例です。「実際の」言語の基礎として、一種の高レベルの「マシン」言語として考えられていたからです。「実際の」言語は決して実現しなかった。LISPマシンは、ハードウェアで(ほとんどの)LISPを実行するという考えに基づいて構築されました。LISPインタープリターは単なるプログラムであるため、原則としてそれを回路に実装することが可能です。実用的ではないかもしれません。しかし、不可能にはほど遠い。

さらに、通常「CPU」と呼ばれるシリコンでプログラムされた多くのインタープリターがあります。そして、マシンコードを(まだ存在しておらず、手元にない…)解釈することがしばしば有用です。たとえば、Linuxのx86_64は最初にエミュレータで作成され、テストされました。チップが市場に登場したとき、初期の採用者/テスターだけでさえ、完全なディストリビューションがありました。Javaは多くの場合、JVMコードにコンパイルされます。JVMコードは、シリコンで書くのが難しくないインタープリターです。

ほとんどの「解釈された」言語は、内部形式にコンパイルされ、最適化されてから解釈されます。これは、例えばPerlとPythonが行うことです。Unixシェルのように、解釈対象の言語用のコンパイラもあります。一方、伝統的にコンパイルされた言語を解釈することは可能です。私が見たやや極端な例の1つは、拡張言語として解釈された C を使用したエディターでした。Cは通常の、しかし単純なプログラムを問題なく実行できます。

一方、最新のCPUは「マシン言語」入力を受け取り、それを下位レベルの命令に変換します。これらの命令は、実行のために引き渡される前に、再配列および最適化(つまり「コンパイル」)されます。

この「コンパイラー」対「インタープリター」の違いは本当に意味がありません。スタックのどこかに「コード」を取得して「直接」実行する究極のインタープリターがあります。プログラマーからの入力は、線に沿って変換されます。これは、「コンパイル」と呼ばれ、砂の中に任意の線を描くだけです。


1

現実には、基本プログラムの解釈とアセンブラーの実行には大きな違いがあります。また、(ジャストインタイム)コンパイラの有無にかかわらず、Pコードとバイトコードの間に領域があります。それで、私はこの現実の文脈でいくつかのポイントを要約しようとします。

  • ソースコードの解析方法が実行時の条件に依存する場合、コンパイラの記述が不可能になるか、だれも気にしないほど難しくなります。

  • 通常、それ自体を変更するコードはコンパイルできません。

  • evalに似た関数を使用するプログラムは、通常、事前に完全にコンパイルすることはできません(プログラムに含まれる文字列をプログラムの一部と見なす場合)。ただし、evalされたコードを繰り返し実行する場合は、 evalのような関数にコンパイラーを起動させると便利です。いくつかの言語は、これを簡単にするためにコンパイラにAPIを提供します。

  • 名前で物事を参照する機能はコンパイルを妨げるものではありませんが、テーブルが必要です(前述のとおり)。関数を名前で呼び出す(IDispatchなど)には、多くの配管作業が必要です。ほとんどの人は、関数呼び出しインタープリターについて効果的に話していることに同意するだろうと思います。

  • 弱いタイピング(定義が何であれ)は、コンパイルを難しくし、おそらく結果の効率を低下させますが、異なる値が異なる解析をトリガーしない限り、多くの場合不可能ではありません。ここにはスライドスケールがあります:コンパイラが実際の型を推測できない場合は、ブランチ、関数呼び出しなどを提供する必要があります。


1

私は、言語のコンパイラを不可能にするプログラミング言語の主な機能(厳密には、セルフホスティングも参照)が自己修正機能であると推測します。言語の意味は、実行時にソースコードを変更することを可能にします(コンパイラーが生成する、固定および静的、オブジェクトコードはできません)。古典的な例は、Lispです同質性も参照)。同様の機能は、多くの言語(javaScriptなど)に含まれるevalなどの言語構造を使用して提供されます。Evalは実際に実行時にインタープリターを(関数として)呼び出します

つまり、言語は独自のメタシステムを表すことができます(メタプログラミングも参照)

言語リフレクションは、特定のソースコードのメタデータについてクエリし、メタデータのみを変更するという意味で(JavaやPHPのリフレクションメカニズムのような)既にコンパイラに問題ないこと注意しください。コンパイル時にメタデータを作成し、必要に応じて、必要に応じてコンパイル済みプログラムで使用できるようにします。

コンパイルを困難にするか、最良のオプションではない(不可能ではない)別の機能は、言語で使用されるタイピングスキーム(つまり、動的タイピングvs静的タイピングおよび強力タイピングvsルーズタイピング)です。これにより、コンパイラはコンパイル時にすべてのセマンティクスを持つことが難しくなります。そのため、コンパイラーの一部(つまり、インタープリター)は、実行時にセマンティクスを処理する生成コードの一部になります。つまり、これはコンパイルではなく解釈です。


LISPはsprtとして考えられていたので、恐ろしい例です-vonbrand 16
1

@vonbrand、多分しかし、ホモイコニシティの概念と統一されたデータコードの二重性の両方を表示
ニコスM.

-1

元の質問はうまく形成されていないように感じます。質問の著者は、やや異なる質問をするつもりだったかもしれません:プログラミング言語のどの特性が、そのためのコンパイラを書くのを促進しますか?

たとえば、コンテキストに依存する言語よりも、コンテキストに依存しない言語のコンパイラを書く方が簡単です。言語を定義する文法には、曖昧さなど、コンパイルが困難になる問題もあります。このような問題は解決できますが、追加の努力が必要です。同様に、無制限の文法で定義された言語は、コンテキスト依存言語よりも解析が困難です(Chomsky Hierarchyを参照)。私の知る限り、最も広く使用されている手続き型プログラミング言語はコンテキストに近いものですが、いくつかのコンテキスト依存要素があり、比較的簡単にコンパイルできます。


2
質問は明らかにコンパイラーとインタープリターに反対/比較するつもりです。これらは異なる動作をする可能性があり、通常は上記の@Raphael制限の場合を除いて機能しますが、構文分析とあいまいさに関してまったく同じ問題があります。したがって、構文を問題にすることはできません。また、過去にありましたが、今日のコンパイラー作成では、構文の問題は通常大きな問題ではないと考えています。私は賛否両論ではありません。コメントすることを好みます。
babou

-1

質問の正解は非常に明白なので、通常は些細なこととして見落とされます。しかし、それは多くのコンテキストで重要であり、インタープリター言語が存在する主な理由です。

ソースコードがまだない場合、ソースコードをマシンコードにコンパイルすることはできません。

通訳者は柔軟性を追加し、特に、基礎となるプロジェクトがコンパイルされたときに利用できなかったコードを実行する柔軟性を追加します。


2
「ソースコードを失いました」はプログラミング言語の特性ではなく、特定のプログラムの特性であるため、質問には答えません。そして、ソースコードの損失を避けることが「インタープリター言語が存在する主な理由」である、またはそれらが存在する理由でさえあるという主張の引用が間違いなく必要です。
デビッドリチャービー14

1
@DavidRicherby tyleriが念頭に置いているユースケースは、インタラクティブな解釈、つまり実行時に入力されるコードです。ただし、それは言語の特徴ではないため、質問の範囲外であることに同意します。
ラファエル

@DavidRicherbyとRaphael、この投稿の著者は、(特定のプログラムのアーティファクトではなく、設計による言語構成である)自己修正機能として(私の答えで説明していることを)暗示していると言います
Nikos M.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.