独学の目的で、動的言語用の単純な仮想マシンを実装したいのですが、C を好みます。Lua VM、Parrot、または Python VM のようなものですが、より単純です。既存の VM のコードと設計に関するドキュメントを参照する以外に、これを達成するための優れたリソースやチュートリアルはありますか?
編集:なぜ投票を閉じるのですか?わかりません-これはプログラミングではありませんか。私の質問に特定の問題がある場合はコメントしてください。
独学の目的で、動的言語用の単純な仮想マシンを実装したいのですが、C を好みます。Lua VM、Parrot、または Python VM のようなものですが、より単純です。既存の VM のコードと設計に関するドキュメントを参照する以外に、これを達成するための優れたリソースやチュートリアルはありますか?
編集:なぜ投票を閉じるのですか?わかりません-これはプログラミングではありませんか。私の質問に特定の問題がある場合はコメントしてください。
単なるインタープリターではなく、仮想マシンが必要だと思います。それらは連続体の2つのポイントだと思います。インタープリターは、プログラムの元の表現に近いものに取り組みます。VM は、より原始的な (自己完結型の) 命令で動作します。これは、一方を他方に変換するためのコンパイル段階が必要であることを意味します。最初にそれに取り組みたいのか、それとも入力構文をまだ念頭に置いているのかはわかりません。
動的言語の場合、データを (キーと値のペアとして) 格納する場所と、それに作用するいくつかの操作が必要です。VM はストアを維持します。その上で実行されるプログラムは、一連の命令 (制御フローを含む) です。一連の命令を定義する必要があります。次のような単純なセットから始めることをお勧めします。
多くの VM がそうであるように、演算にはスタックベースの計算アプローチを使用することをお勧めします。上記にはまだあまり動的なものはありません。これを達成するには、実行時に変数の名前を計算する機能 (これは単に文字列操作を意味します) と、コードをデータとして処理する機能の 2 つが必要です。これは、関数参照を許可するのと同じくらい簡単かもしれません。
VM への入力は、理想的にはバイトコードです。コンパイラをまだ入手していない場合、これは基本的なアセンブラ (VM の一部である可能性があります) から生成できます。
VM 自体はループで構成されています。
1. Look at the bytecode instruction pointed to by the instruction pointer.
2. Execute the instruction:
* If it's an arithmetic instruction, update the store accordingly.
* If it's control flow, perform the test (if there is one) and set the instruction pointer.
* If it's print, print a value from the store.
3. Advance the instruction pointer to the next instruction.
4. Repeat from 1.
計算された変数名の処理は難しい場合があります。計算された名前が含まれる変数を命令で指定する必要があります。これは、命令が入力で提供される文字列定数のプールを参照できるようにすることで実現できます。
サンプルプログラム (アセンブリとバイトコード):
offset bytecode (hex) source
0 01 05 0E // LOAD 5, .x
3 01 03 10 // .l1: LOAD 3, .y
6 02 0E 10 0E // ADD .x, .y, .x
10 03 0E // PRINT .x
12 04 03 // GOTO .l1
14 78 00 // .x: "x"
16 79 00 // .y: "y"
暗示される命令コードは次のとおりです。
"LOAD x, k" (01 x k) Load single byte x as an integer into variable named by string constant at offset k.
"ADD k1, k2, k3" (02 v1 v2 v3) Add two variables named by string constants k1 and k2 and put the sum in variable named by string constant k3.
"PRINT k" (03 k) Print variable named by string constant k.
"GOTO a" (04 a) Go to offset given by byte a.
変数が他の変数などによって名前が付けられた場合などのバリアントが必要です (そして、間接的なレベルは推論するのが難しくなります)。アセンブラは "ADD .x, .y, .x" のような引数を見て、(計算された変数ではなく) 文字列定数から追加するための正しいバイトコードを生成します。
それは C で VM を実装することではありませんが、この質問を見る前に開いていた最後のタブだったので、タグを使用して JavaScript でQBASIC バイトコード コンパイラと仮想マシンを実装することに関する記事を指摘する必要があるように感じます。<canvas>
表示用。「ニブル」ゲームを実行するのに十分な QBASIC を実装するためのすべてのソース コードが含まれており、コンパイラとバイトコード インタープリタに関する一連の記事の最初の記事です。これは VM について説明しており、コンパイラについても説明する今後の記事を約束しています。
ところで、私はあなたの質問をクローズするために投票しませんでしたが、あなたが得たクローズ投票は、仮想マシンの実装について学ぶ方法に関する昨年の質問の複製でした。この質問(チュートリアルまたは比較的単純なものに関するもの)は、それとは十分に異なるため、開いたままにしておく必要があると思いますが、さらにアドバイスが必要な場合は、その質問を参照することをお勧めします.
注目すべきもう 1 つのリソースは、Lua 言語の実装です。性能に定評のあるレジスターベースのVMです。ソース コードはANSI C89 であり、一般的に非常に読みやすいです。
ほとんどの高性能スクリプト言語と同様に、エンド ユーザーは読みやすい高レベルの動的言語 (クロージャー、テール コール、不変文字列、数値とハッシュ テーブルなどの機能をプライマリ データ型として、関数をファースト クラスの値として使用するなど) を認識します。 . ソース テキストは VM のバイトコードにコンパイルされ、VM 実装によって実行されます。VM 実装の概要はEdmund's answerで説明されているとおりです。
VM 自体の実装の移植性と効率性を維持するために多大な努力が払われました。さらにパフォーマンスが必要な場合は、VM バイト コードからネイティブ命令へのジャスト イン タイム コンパイラが 32 ビット x86 用に存在し、64 ビット用のベータ リリースです。
開始するには( C ではなく C++ であっても)、muParserを参照できます。
これは、単純な仮想マシンを使用して操作を実行する数式パーサーです。すべてを理解するには時間が必要だと思います。とにかく、このコードは、実際の完全なプログラムを実行できる完全な VM よりも単純です。(ちなみに、私はC# で同様の libを設計しています - これは初期段階ですが、次のバージョンでは .NET/VM ILまたは muParser のような新しい単純な VMへのコンパイルが可能になるでしょう)。
もう 1 つの興味深い機能はNekoVM (.n バイトコード ファイルを実行する) です。これは C で書かれたオープン ソース プロジェクトであり、そのメイン言語 (.neko) はソース ツー ソース コンパイラテクノロジによって生成されると考えられています。前のトピックの精神で、同じ作者のHaxeを参照してください (オープンソースも)。
私はパーティーに遅れましたが、Game Scripting Mastery をお勧めします。これは、動作するスクリプト言語とその VM をゼロから作成するのに役立ちます。そして、前提条件はほとんどありません。