この記事では、私の作業の主要な段階を強調し、言語の作成された概念と、現在作業中の最初の実装について説明します。
事前にプロジェクト全体をFree Pascalで書いたと言います。なぜなら、 その上にあるプログラムは膨大な数のプラットフォーム向けにアセンブルでき、コンパイラー自体が非常に最適化されたバイナリーを生成します(プロジェクトのすべてのコンポーネントをO2フラグで収集します)。
言語ランタイム
最初に話すのは、将来のアプリケーションを自分の言語で実行するために作成しなければならなかった仮想マシンです。 スタックアーキテクチャを実装することにしたのは、おそらくそれが最も簡単な方法だったからです。 ロシア語でこれを行う方法に関する通常の記事は1つも見つかりませんでした。そのため、英語の資料を読んだ後、自転車の設計と作成に取りかかりました。 さらに、この問題に関する「高度な」アイデアと開発を提供します。
スタック実装
明らかに、VMはスタックの先頭にあります。 私の実装では、ブロックで動作します。 本質的に、これはポインタの単純な配列であり、スタックの最上部のインデックスを格納するための変数です。
初期化されると、256要素の配列が作成されます。 さらに多くのポインターがスタックにスローされると、そのサイズは次の256要素分増加します。 したがって、スタックからアイテムを削除するとき、そのサイズは調整可能です。
VMはいくつかのスタックを使用します。
- メインスタック。
- リターンポイントを格納するためのスタック。
- ガベージコレクターのスタック。
- try / catch / finallyブロックのスタックハンドラー。
定数と変数
これにより、すべてがシンプルになります。 定数は別の小さなコードで処理され、将来のアプリケーションでは静的アドレスで使用できます。 変数は特定のサイズのポインターの配列であり、そのセルへのアクセスはインデックスによって実行されます-つまり 静的アドレス。 変数はスタックの最上部に配置するか、そこから読み取ることができます。 実際、 変数は基本的にVMのメモリに値へのポインターを格納し、言語の主な暗黙のポインターで動作します。
ガベージコレクター
私のVMでは、半自動です。 つまり 開発者は、ガベージコレクターをいつ呼び出すかを決定します。 同じPython、Perl、Ruby、Luaなどのように、通常のポインターカウンターに従っては機能しません。 これは、マーカーシステムを介して実装されます。 つまり 変数に一時的な値が割り当てられていると想定される場合、この値へのポインターがガベージコレクタースタックに追加されます。 将来的には、コレクターはすでに準備済みのポインターのリストをすばやく実行します。
try / catch / finallyブロックの処理
現代の言語と同様に、例外処理はその重要なコンポーネントです。 VMのカーネルはtry..catchブロックにラップされています。このブロックは、例外に関する小さな情報をスタックに置くことで、例外をキャッチした後、コード実行に戻ることができます。 アプリケーションコードでは、コードのtry / catch / finallyブロックを指定して、キャッチするエントリポイント(例外ハンドラー)およびfinally / end(ブロックの終わり)を指定できます。
マルチスレッド
VMレベルでサポートされています。 シンプルで使いやすいです。 割り込みシステムなしで動作するため、コードは複数のスレッドでそれぞれ数倍速く実行する必要があります。
VMの外部ライブラリ
それなしで行う方法はありません。 VMは、他の言語で実装されているように、インポートをサポートしています。 Mashでコードの一部を作成し、ネイティブ言語でコードの一部を作成してから、それらをリンクできます。
高レベル言語MashからVMのバイトコードへの翻訳者
中級言語
複雑な言語からVMコードへの翻訳者をすばやく作成するために、私は最初に中間言語を開発しました。 アセンブラーのような恐ろしい光景が判明しましたが、ここでは特別な意味はありません。 このレベルでは、翻訳者はほとんどの定数、変数を処理し、静的アドレスとエントリポイントのアドレスを計算します。
トランスレータアーキテクチャ
実装に最適なアーキテクチャを選択しませんでした。 翻訳者は、他の翻訳者に適しているため、コードツリーを構築しません。 彼は建設の始まりを見ます。 つまり 解析されたコードが「while <条件>:」のように見える場合、これはループのwhile構築であり、whileループ構築として処理する必要があることは明らかです。 複雑なスイッチケースのようなもの。
このアーキテクチャソリューションのおかげで、翻訳者はそれほど速くありませんでした。 ただし、その洗練のシンプルさが大幅に向上しています。 コーヒーを冷やすよりも早く必要なデザインを追加しました。 OOPの完全なサポートは1週間以内に実装されました。
コードの最適化
ここでは、もちろん、よりよく実現できます(そして、手が届くと、後で実現します)。 これまでのところ、オプティマイザーは未使用のコード、定数、およびアセンブリからのインポートを遮断する方法のみを知っています。 また、同じ値を持ついくつかの定数は1つに置き換えられます。 以上です。
マッシュ言語
言語の基本概念
主なアイデアは、最も機能的でシンプルな言語を開発することでした。 私は、開発がそのタスクに大胆に対処すると信じています。
コードブロック、プロシージャ、および関数
言語のすべての構成はコロンで開き、 終了演算子で閉じます。
プロシージャと関数は、それぞれprocとfuncとして宣言されます。 引数は括弧内にリストされています。 他のほとんどの言語と同じように。
returnステートメントは関数から値を返すことができます。breakステートメントを使用すると、プロシージャ/関数を終了できます(ループ外にある場合)。
コード例:
... func summ(a, b): return a + b end proc main(): println(summ(inputln(), inputln())) end
- : for..end, while..end, until..end
- : if..[else..]end, switch..[case..end..][else..]end
- : proc <>():… end, func <>():… end
- Label & goto: <>:, jump <>
- Enum .
, var .
:
a ?= 10 b ?= a + 20
var a = 10, b = a + 20
.
. Mash - . .. , , ( .. ), ().
, .
:
uses <bf> uses <crt> class MyClass: var a, b proc Create, Free func Summ end proc MyClass::Create(a, b): $a = new(a) $b = new(b) end proc MyClass::Free(): Free($a, $b) $rem() end func MyClass::Summ(): return $a + $b end proc main(): x ?= new MyClass(10, 20) println(x->Summ()) x->Free() end
: 30.
:
uses <bf> uses <crt> class MyClass: var a, b proc Create, Free func Summ end proc MyClass::Create(a, b): $a = new(a) $b = new(b) end proc MyClass::Free(): Free($a, $b) $rem() end func MyClass::Summ(): return $a + $b end class MyNewClass(MyClass): func Summ end func MyNewClass::Summ(): return ($a + $b) * 2 end proc main(): x ?= new MyNewClass(10, 20) println(x->Summ()) x->Free() end
: 60.
? !:
uses <bf> uses <crt> class MyClass: var a, b proc Create, Free func Summ end proc MyClass::Create(a, b): $a = new(a) $b = new(b) end proc MyClass::Free(): Free($a, $b) $rem() end func MyClass::Summ(): return $a + $b end class MyNewClass(MyClass): func Summ end func MyNewClass::Summ(): return ($a + $b) * 2 end proc main(): x ?= new MyClass(10, 20) x->Summ ?= MyNewClass::Summ println(x->Summ()) x->Free() end
: 60.
:
uses <bf> uses <crt> class MyClass: var a, b end proc main(): x ?= new MyClass println(BoolToStr(x->type == MyClass)) x->rem() println(BoolToStr(typeof(3.14) == typeReal)) end
: true, true.
?= .
= .
. .
@<> — .
?<> — .
@= — .
:
uses <bf> uses <crt> proc main(): var a = 10, b b ?= @a PrintLn(b) b ?= ?b PrintLn(b) b++ PrintLn(a) InputLn() end
: - , 10, 11.
Try..[catch..][finally..]end
:
uses <bf> uses <crt> proc main(): println("Start") try: println("Trying to do something...") a ?= 10 / 0 catch: println(getError()) finally: println("Finally") end println("End") inputln() end
GraalVM & Truffle. JIT , . , JIT GraalVM LLVM.
.
GitHub
, , .