(このドキュメントは進行中の作業です…)
注:「CPP」ブランチには、このプロジェクトの「ダム」ポートがCからC ++に含まれています。これは、いくつかの理由で行いました。
J2は、ミニマリストのプログラミング言語(「edict」、「実行可能辞書」用)とc反射/FFIシステムを組み合わせたシステムです。共有されたライブラリを簡単にインポートし、「接着剤」コードを作成する必要なく、その内部(少なくともグローバルスコープの変数)を直接使用することができます。
たとえば、「inc.c」を含むファイルがある場合:
typedef struct mystruct { int i; float f; } mystruct;
extern mystruct increment(mystruct x) { x.i+=1; x.f+=1; return x; }
...そして私はそれから共有ライブラリを作ります:
gcc --shared -g inc.c -o inc.so
...その後、その共有ライブラリをedict通訳にインポートして、それを使用することができます。
e> loadlib([inc.so]) @mylib
Finished curating module
e> mylib<mystruct! <2@i 4@f> @x increment(x)>/ stack!
VMRES_STACK
<null>
structure_type "struct mystruct"
member "i"
base_type "int" 0x3 (0x7f361c0121a0)
member "f"
base_type "float" 5 (0x7f361c0121a4)
シンプルに見えますが、ここのカバーの下で多くのことが起こっています...
「edict」は、cタイプ、変数、および方法への「ネイティブ」アクセスを任意の複雑さの「ネイティブ」アクセスを提供することにより、C型、変数、および方法への「ネイティブ」アクセスを提供することにより、その単純さを補うミニマリストプログラミング言語です。ラッパーまたは接着コード。または、それを、実行時に自分の内部に動的にアクセスできるようにするCプログラムのリフレクションライブラリとして見ることができます。
言語は、3つの要素の基礎の上に構築されています。
これらの3つの要素は、実行時に多目的プログラム可能な環境に自分自身を組み立てます。システムは次のようにブートストラップします。
dict令の進化は、フォース、lisp、joy、factor、tcl、mathematicaなどの影響を受けています。
キーポイント:
while (call=vm_dispatch(call)); // VM's inner loop
(注:私はこの「Zigzag」構造を見つけました。
「Listree」は、e令のコアデータ構造とそれを実装するVMです。 Listreeのインスタンスは、単にListreeの価値で構成されています。
Listree Valueが他のListree値への参照を(ラベル付け)する可能性は、Listreeを「階層的」または「再帰的」データ構造にするものです。 Listreeは、Edictの中核とそれを実装するシステムの両方にあります。
*この単純な説明では明示的ではありません(意図的です)が、実際には各ラベルは他のListree値への参照のリストを指します。
前述のように、VMは、「データスタック」や「辞書」サブリーストルなど、すべての状態をListree値内で維持しています。
dict令では、2種類のデータ値がありますが、両方のタイプの値は、VMのデータスタックまたはその辞書内に存在するListree値を使用して保存されます。
最も単純なタイプの価値は「リテラル」です。リテラルは単なるテキストで、四角い括弧で描写されています。
[This is a literal]
LISPのバックグラウンドから来た場合、通訳はリテラルをS-Expressionsに分解すると思うかもしれませんが、そうではありません。ブラケットの間のすべては、Listree Valueの「データバッファー」で「文字通り」表されます。
ed令の通訳は、ネストされた四角い括弧を追跡しているので、
[This is a [nested] literal]
「これは[ネストされた]リテラルです」という値を持つ単一のリテラルとして解釈されます。
文字通りの定義に表示される「」キャラクターは、次のキャラクターを逃れ、通訳が(たとえば)不均衡な四角い括弧を含むリテラルを作成できるようにします。
[This is a literal containing an unbalanced [ bracket]
インタープリターが1つに出くわすと、文字通りの値(またはむしろ、値への参照- 重要な区別)がデータスタックに単純に配置されます。
Otoh、通訳者は、文字通りではないものをもう少し処理します。 (リテラルではない他の種類の値がありますが、後のセクションで説明します…)
edictプログラマーは、スタック上の値に名前を割り当て、その後、それらの名前でそれらの値を参照できます。割り当ては単にこのように見えます:
@mylabel
値への名前の割り当ては実際にいくつかのことを行います。
通訳者がラベルへの参照を表示すると、その参照は、そのラベルに関連付けられた値への参照に置き換えられます。
dictは、1つの重要な方法で他の言語とは異なります。多くの言語は「ホモコニック」であり、IEコードとデータは同じ基礎構造を使用して表されます。 (LISPは、ホモコニック語の伝統的な例です。)ed令は、ホモコニックでも非ホモコニックでもありません。「機能」はまったくありません。値に適用できる評価オペレーターがあるだけです。
dict令の基本的な「機能のような」ものは、次のように見えるかもしれません。
[1@x]
(「x」というラベルを値「1」に割り当てます。)
それはただ文字通りであることに注意してください。
それを呼び出すために、評価演算子が使用されます:
[1@x]!
これの結果は、通訳者が次のことを直接読んだ場合とまったく同じです。
1@x
評価オペレーターは、スタックの上部の内容物をインタープリターに供給することです。
さて:ラベルを値に割り当てることができ、ラベルを呼び出して値をリコールできること、そしてそれらの値がスタックにプッシュされ、評価演算子がスタックの内容をインタープリターに戻すことを思い出してください。
[1@x]@f
f!
この小さなシーケンスは次のとおりです。
edictは、ライブラリのデバッグセクション(dwarf)を介して、利用可能な場合は、Cライブラリタイプとグローバル変数/関数情報をインポートできます。ドワーフ情報は辞書に処理および保存され、VMはこの情報を「理解」し、リテラル値で動作する同じ単純な「ネイティブ」構文を使用してedict通訳内に提示できます。
int! @x
MyCGlobalInt @y
xy Multiply!
(以下を塗る…)
ref | 参照 |
- ref | 参照(テール) |
@ | TOSへの割り当て |
@ref | 参照への割り当て |
/ | TOSをリリースします |
/ref | リリースref |
^ref | メタレファレンス |
ref^ | 上部スタックレイヤーを参照します |
^ref^(^ref +^) | メタレファレンスとマージ上のスタックレイヤーを参照してください |
! | TOSを評価します |
tos <...> | 「Dict-Contextで評価する」:TOSを押して、<...>の内容を評価し、DICTスタックのポップトップをTOSに評価します。 |
TOS(...) | 「コードコンテキストでの評価」:NULLスタック/DICTレイヤーを押し、TOSをコードスタックに押し、パレンズの内容を評価し、コードスタックの上部を評価し、DICTの上部を破棄し、スタックの上部を前のレイヤーに連結します。 |
簡単な反射プログラム:
int! [3]@ square! stack!
壊す:
int | スタックに「int」(ネイティブCタイプ)の検索値とプッシュ値 |
! | トップオブスタックを評価します。この場合、「int」のインスタンスを割り当てます |
[3] | 文字通りの「3」をスタックに押し込みます |
@ | 割り当て;この場合、文字列「3」は自動的にC“ int”に強制されます |
square | 「正方形」(ネイティブCメソッド)の値をスタックに検索して押します |
! | トップオブスタックを評価します。この場合、ネイティブCメソッド「四角」へのFFI呼び出し |
stack | 「スタック」(ネイティブCメソッド)の値をスタックに検索して押します |
! | スタックのトップを評価してください... |
int 0x9、すなわち3四角が表示されます。
C整数の明示的な作成と割り当ては、例に対してのみ表示されます。より簡単なバージョンは次のとおりです。
[3] square! stack!
同じ強制は、FFI引数のマーシャリング中に自動的に実行されていました。
「コード」は自動的に評価されないことに注意してください。評価は「!」を介して明示的に呼び出されます。コードは、「実行」することを決定するまでのデータです。
[3] square stack! | スタック上のデータと「コード」、未評価 |
! stack! | TOSを評価し、結果を観察します |
要因:
[@n int_iszero(n) 1 | int_mul(fact(int_dec(n)) n)]@fact
Listreeでは、値にはキー/CLLペアの辞書が含まれています。各CLL(「循環リンクリスト」、二重端キューを実装)には、それぞれがの辞書が含まれている値への1つ以上の参照が含まれています。 .. 等々。 Listreeのキー/DEQコンポーネントは、Arne Anderssonの「シンプルな」RBTreeバリエーションに基づくBST実装です。 (http://user.it.uu.se/~arnea/ps/simp.pdf)
VMは非常に簡単です。バイトコードを評価し、それぞれがバイトコードを実装するCメソッドの配列へのインデックスです。
リセット | クリアライト |
内線 | 文字シーケンスをデコードして照明を設定します(「[1つ2 3]」、「ABC」) |
ext_push | データスタックに点灯します |
ref | LITから辞書参照refを作成します |
deref | 辞書でRefを解決します |
割り当てる | データスタックのトップをポップして、場所を参照して辞書に挿入します( "@") |
取り除く | Refでのリリース値 |
評価します | データスタックのポップトップとa)ffiを呼び出す、またはb)コードスタックをプッシュしてVMを降伏させる |
ctx_push | データスタックのトップをポップして辞書スタックのヘッドに押し込み、新しいレイヤーをデータスタックに押します |
ctx_pop | データスタックの上位2層、辞書スタックのポップヘッドを融合し、それをデータスタックに押します |
fun_push | データスタックのポップトップとFUNCスタックをプッシュし、レイヤーをデータスタックに追加し、辞書にヌルレイヤーを追加します |
fun_eval | {fun_pop}をプッシュしてスタックをコードし、funcスタックのポップトップ、「eval」を実行します |
fun_pop | 上位2つのスタックレイヤーを融合し、ヌル辞書層を破棄します |
投げる | 例外を投げる* |
キャッチ | 例外をキャッチ* |
*例外は、VMエラーと条件の両方がどのように実装されているかであり、VMの状態を平均操作よりも少しずついじり、独自の段落に値します。
この単純な一連の操作は、辞書と対話し、言語構成を再帰的に評価するのに十分であり(VMはスタックレスの実装です)、最も重要なことは、Cタイプ、関数、およびデータを活用することです。これは、その能力を簡単に拡張するために、Cへの緊密な結合に依存しているベアボーンフレームワークです。
(塩に値するGNU/Linuxディストリビューションにはこれらがあります。
実行する:GCC、Cmake、およびライブラリがあまり古くないと仮定すると、単に「作成」して再構築してREPLに入ります。