元のインタープリターから独立した「ブートストラップされた」インタープリターを作成することは可能ですか?


21

ウィキペディアによると、コンパイラを記述するという文脈での「ブートストラップ」という用語はこれを意味します

コンピューターサイエンスでは、ブートストラップは、コンパイルするソースプログラミング言語でコンパイラー(またはアセンブラー)を記述するプロセスです。この手法を適用すると、セルフホスティングコンパイラが実現します。

そして、それがどのように機能するかを理解できます。しかし、話は通訳者にとっては少し違うようです。もちろん、セルフホスティングのインタープリターを作成することもできます。それは私が求めていることではありません。私が実際に求めていることである:それは、元の、最初のインタプリタの自己ホスト型インタプリタから独立させることが可能です。私の言いたいことを説明するために、この例を考えてみましょう。

最初のインタープリターバージョンを言語Xで記述し、インタープリターはYと呼ばれる作成中の新しい言語用です。最初に言語Xのコンパイラを使用して実行可能ファイルを作成します。これで、言語Xで作成されたインタープリターを使用して、新しい言語Yで作成されたファイルを解釈できます。

私が理解している限りでは、言語Xで作成したインタープリターを「ブートストラップ」できるようにするには、言語Yでインタープリターを書き換える必要があります。ただし、ここで問題があります。言語Yでインタープリター全体を書き換えたとしても、言語Xで作成した元のインタープリターが必要になります。インタープリターを言語Yで実行するには、ソースファイルを解釈する必要があります。しかし、ソースファイルを正確に解釈するにはどうすればよいでしょうか?もちろん、何もありえないので、最初のインタープリターを使用する必要があります。

言語Yで作成する新しいインタープリターの数に関係なく、Xで記述された最初のインタープリターを使用して後続のインタープリターを常に解釈する必要があります。これは単に通訳者の性質のために問題のようです。

ただし逆に、通訳に関するこのウィキペディアの記事では、実際に自己ホスト型通訳について説明しています。関連する小さな抜粋を次に示します。

自己通訳とは、自身を解釈できるプログラミング言語で書かれたプログラミング言語インタープリターです。例は、BASICで書かれたBASICインタプリタです。セルフインタープリターは、セルフホスティングコンパイラに関連しています。

解釈対象の言語用のコンパイラーが存在しない場合、セルフインタープリターを作成するには、ホスト言語(別のプログラミング言語またはアセンブラー)で言語を実装する必要があります。このような最初のインタープリターを持つことにより、システムはブートストラップされ、言語自体でインタープリターの新しいバージョンを開発できます

しかし、これがどのように行われるかは、まだはっきりしていません。何であれ、ホスト言語で書かれたインタープリターの最初のバージョンを常に使用せざるを得ないようです。

さて、上記の記事は、ウィキペディアがセルフホスティングインタプリタと思われるいくつかの例を提供している別の記事にリンクしています。しかし、よく調べてみると、これらのセルフホスティングインタープリターの多くの主な「解釈」部分(特にPyPyやRubiniusなどのより一般的なもの)は、実際にはC ++やCなどの他の言語で書かれているようです。

それで、私が上で説明したことは可能ですか?自己ホスト型インタープリターは元のホストから独立できますか?もしそうなら、これはどのように正確に行われますか?

回答:


24

簡単な答えは次のとおりです。あなたは疑う余地があります。常にXで書かれた別のインタープリターか、Yから既にインタープリターを持っている他の言語へのコンパイラーが必要です。インタープリターが実行され、コンパイラーはある言語から別の言語にのみ翻訳します。システムのある時点で、インタープリターが必要になります(それがCPUであっても)。

言語Yで作成する新しいインタープリターの数に関係なく、Xで記述された最初のインタープリターを使用して後続のインタープリターを常に解釈する必要があります。これは単に通訳者の性質のために問題のようです。

正しい。何ができるやっていることから、コンパイラ書き込みであるYX(またはあなたが通訳を持っている別の言語)を、あなたもでそれを行うことができますY。次に、あなたの実行可能なYの中に書かれたコンパイラYYで書かれたインタプリタX(または上のYで書かれたインタプリタY上で実行されているYで書かれたインタプリタX、または上のY用に書かれたインタプリタY上で実行されているYで書かれたインタプリタをYで実行されているY書かれたインタプリタXあなたコンパイルする、または...無限)のYで書かれたインタプリタYXを使用すると、その後にそれを実行できるように、Xのインタプリタ。そうすれば、あなたはあなたの脱却ているYの中に書かれたインタプリタXが、今は必要なXの(そうでない場合は、私たちが走っていない可能性があるので、我々は、しかし、我々は既に持っていることを知っているインタプリタをX用に書かれたインタプリタYを)、そしてあなた最初にY -to- X-コンパイラを作成する必要がありました。

ただし、逆に、ウィキペディアの通訳に関する記事では、実際にセルフホスティングの通訳について説明しています。関連する小さな抜粋を次に示します。

自己通訳とは、自身を解釈できるプログラミング言語で書かれたプログラミング言語インタープリターです。例は、BASICで書かれたBASICインタプリタです。セルフインタープリターは、セルフホスティングコンパイラに関連しています。

解釈対象の言語用のコンパイラーが存在しない場合、セルフインタープリターを作成するには、ホスト言語(別のプログラミング言語またはアセンブラー)で言語を実装する必要があります。このような最初のインタープリターを持つことにより、システムはブートストラップされ、言語自体でインタープリターの新しいバージョンを開発できます

しかし、これがどのように行われるかは、まだはっきりしていません。何であれ、ホスト言語で書かれたインタープリターの最初のバージョンを常に使用せざるを得ないようです。

正しい。ウィキペディアの記事では、言語の2番目の実装が必要であると明示的に記載されており、最初の言語を削除できるとは記載されていないことに注意してください。

さて、上記の記事は別の記事にリンクしています。この記事では、Wikipediaがセルフホスティングインタープリターの例を示しています。よく調べてみると、これらのセルフホスティングインタープリターの多くの主な「解釈」部分(特にPyPyやRubiniusなどのより一般的なもの)は、実際にはC ++やCなどの他の言語で書かれているようです。

繰り返しますが これらは本当に悪い例です。たとえば、Rubiniusを取り上げます。はい、RubiniusのRuby部分が自己ホスト型であることは事実ですが、それはコンパイラーであり、インタープリターではありません。RubyソースコードにコンパイルしてRubiniusバイトコードに変換します。インタプリタ部分のOTOHは自己ホスト型ではありません。Rubiniusバイトコードを解釈しますが、C ++で記述されています。したがって、Rubiniusを「自己ホスト型インタープリター」と呼ぶのは間違っています。自己ホスト型部分はインタープリターではなく、インタープリター部分は自己ホスト型ではありません。

PyPyは似ていますが、さらに不正確です。そもそもPythonでさえ書かれておらず、異なる言語であるRPythonで書かれています。構文的にはPythonに構文的に似ており、意味的には「拡張サブセット」ですが、実際にはJavaとほぼ同じ抽象化レベルの静的型付け言語であり、その実装はRPythonをCソースコードECMAScriptにコンパイルする複数のバックエンドを持つコンパイラーですソースコード、CILバイトコード、JVMバイトコード、またはPythonソースコード。

それで、私が上で説明したことは可能ですか?セルフホストインタープリターは元のホストから独立できますか?もしそうなら、これはどのように正確に行われますか?

いいえ、それ自体ではありません。元のインタープリターを保持するか、コンパイラーを作成して自己通訳者をコンパイルする必要があります。

KleinSelfで記述)やMaxine(Javaで記述)などのメタ循環VM いくつかあります。ただし、ここで「メタ循環」の定義はまだ異なっていることに注意してください。これらのVMは実行する言語で記述されていません。KleinはSelfバイトコードを実行しますが、Selfで記述され、MaxineはJVMバイトコードを実行しますがJavaで記述されます。しかし、VMのセルフ/ Javaのソースコードは、実際にセルフ/ JVMバイトコードにコンパイルして、VMで実行されるので、時間によってVMが実行されます取得し、それがある、それが実行される言語で。ふう。

また、これはSqueakVMJikes RVMなどのVMとは異なることに注意してください。JikesはJavaで記述されており、SqueakVMはSlang(Smalltalkの静的に型付けされた構文およびセマンティックサブセットは、高レベルアセンブラとほぼ同じ抽象化レベルにあります)で記述されており、実行前に静的にネイティブコードにコンパイルされます。彼らは自分の中では走りません。あなたはできる、しかし、それらを実行する上で、トップ自身の(または他のSmalltalk VM / JVMの上)。しかし、それはこの意味で「メタ円形」ではありません。

マキシンとクライン、大藤DO自分の中を走ります。独自の実装を使用して独自のバイトコードを実行します。これは本当に驚くべきことです!VMはユーザープログラムと共に実行されるため、ユーザープログラムからVMへの呼び出しをインラインで実行できます。たとえば、ガベージコレクターまたはメモリアロケーターへの呼び出しをユーザーにインライン化できます。コード、ユーザーコードのリフレクションコールバックをVMにインライン化できます。また、実行中のプログラムを監視し、実際のワークロードとデータに応じて最適化する最新のVMが行う巧妙な最適化トリックのすべては、VMがユーザープログラムの実行中にユーザープログラムの実行中に同じトリックを自分自身に適用できます特定のワークロードを実行しています。つまり、VMはそのために非常に特化し特定のワークロードを実行する特定のプログラム。

ただし、上記の「インタープリター」という言葉の使用を避け、常に「execute」を使用していることに注意してください。さて、これらのVMはインタプリタを中心に構築されているのではなく、(JIT)コンパイラを中心に構築されています。後でMaxineに追加されたインタープリターがありましたが、常にコンパイラーが必要です。VM を別のVM(Maxineの場合はOracle HotSpotなど)の上で1 実行する必要があります。Maxineの場合、独自のブートアップフェーズをJITコンパイルし、コンパイルされたネイティブコードをブートストラップVMイメージにシリアル化し、非常に単純なブートローダーを前に貼り付けます(Cで記述されたVMの唯一のコンポーネントですが、それは便宜上のものです) 、Javaでも可能です)。これで、Maxineを使用して自分自身を実行できます。


Yeesh。セルフホスティングの通訳の世界がこんなに粘着性があるとは知りませんでした!素晴らしい概要を教えてくれてありがとう。
クリスチャンディーン

1
ハハ、まあ、なぜ世界は概念よりも心を曲げるべきではないのですか?;-)
ヨルグWミットタグ

3
問題の1つは、関係する言語で人々がしばしば速くてゆるいことをしていることだと思います。たとえば、Rubiniusは通常「Ruby in Ruby」と呼ばれますが、それは物語の半分にすぎません。はい、厳密に言えば、RubiniusのRubyコンパイラはRubyで記述されていますが、バイトコードを実行するVMはそうではありません。さらに悪いことに、PyPyは「Python in Python」と呼ばれることがよくありますが、実際にはPythonが1行も存在しません。すべてはRPythonで書かれています。RPythonはPythonプログラマーになじみやすいように設計されていますが、Pythonではありません。同様にSqueakVM:それはSmalltalkの中に書かれていません、それ...
イェルクWミッターク

…はSlangで書かれており、実際にコーディングした人によれば、その抽象化機能はCよりもさらに劣っています。Slangの唯一の利点は、Smalltalkの適切なサブセットであるということです。つまり、強力なSmalltalk IDEで開発(および実行し、最も重要なことにVMをデバッグ)できるということです。
ヨルグWミットタグ

2
別の複雑さを追加するために:一部のインタープリター言語(FORTH、および場合によってはTeX)は、実行可能ファイルとして、実行中のシステムの読み込み可能なメモリイメージを書き込むことができます。その意味では、そのようなシステムは元のインタープリターなしで実行できます。たとえば、16ビットバージョンのFORTHを使用して32ビットバージョンを別のCPUに「解釈」し、別のOSで実行することにより、FORTHインタープリターを作成したことがあります。(注、FORTH言語には独自のアセンブラが含まれているため、「FORTH VM」(通常は10または20マシンコード命令のみ)はFORTH自体で記述できます。)
alephzero

7

セルフホスティングインタープリターはそれ自体を実行するためにインタープリターを必要とし、コンパイラーと同じ意味でブートストラップすることはできないことに注意してください。

ただし、自己ホスト型言語は、自己ホスト型インタープリターと同じものではありません。通常、コンパイラーをビルドするよりもインタープリターをビルドする方が簡単です。したがって、新しい言語を実装するには、最初に無関係な言語でインタープリターを実装します。次に、そのインタープリターを使用して、言語用のコンパイラーを開発できます。コンパイラが解釈されるため、言語は自己ホストされます。その後、コンパイラはそれ自体をコンパイルし、完全にブートストラップされたと見なすことができます。

この特殊なケースは、自己ホスト型のJITコンパイルランタイムです。ホスト言語のインタープリターから開始し、新しい言語を使用してJITコンパイルを実装します。その後、JITコンパイラーはそれ自体をコンパイルできます。これは自己ホスト型インタープリターのように感じますが、無限通訳者の問題を回避します。このアプローチが使用されますが、まだあまり一般的ではありません。

別の関連する手法は、拡張可能なインタープリターです。ここでは、解釈される言語で拡張機能を作成できます。たとえば、言語で新しいオペコードを実装できます。これにより、循環依存を回避する限り、ベアボーンインタープリターを機能豊富なインタープリターに変えることができます。

実際に非常に一般的に発生するケースは、解析時の評価マクロなど、言語が独自の解析に影響を与える能力です。マクロ言語は処理中の言語と同じであるため、専用または制限されたマクロ言語よりも機能が豊富になる傾向があります。ただし、拡張を実行する言語は、拡張後の言語とはわずかに異なる言語であることに注意してください。

「本物の」自己ホスト型通訳が使用される場合、これは通常、教育または研究の理由で行われます。たとえば、Scheme内でSchemeのインタープリターを実装することは、プログラミング言語を教えるクールな方法です(SICPを参照)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.