メインコンテンツまでスキップ
バージョン: 0.2.0 snapshot

基本仕様0.2.0

従来の基本命令1〜35は変更していません。データ型と命令36〜57も、この仕様の規範的な部分です。

この文書は実装済みのシードコンパイラを記述します。1.0まではソースの互換性を実験的なものとして扱います。意味を変更する場合は、命令表、検査済みIR、生成後の実行、TypeScript参照実装、フィクスチャを一緒に更新しなければなりません。

字句文法​

source = { token | whitespace } ;
token = "8", shaft, "D", [ flight ] ;
shaft = "=", { "=" } ;
flight = "~", { "~" } ;
whitespace = " " | "\t" | "\r" | "\n" ;

長さ1〜57を定義しています。トークンは隙間なく並べられます。8=D8=D はゼロのリテラル2つです。ASCIIの空白はトークンの区切りや整形に使い、長さには意味を持たせません。CRLFは1つの改行で、単独のCRやLFも改行です。Unicodeの空白、BOM、コメント、識別子、8 以外の十進数字、記号集合の外の句読点は拒否します。空のプログラムも拒否します。ただし、空の入力を整形した結果は空の出力です。

波がない状態は、内部では即値ゼロではなく未指定として表します。そのため、波のない int はゼロを積み、波のない二項算術命令はスタックから2つの値を消費します。波がn個の int はnを積みます。波がn個の算術命令は左オペランドだけを消費し、右オペランドにはnを使います。これにより、別の記号を増やさずに、スタック方式、即値方式、即値の大きさを区別します。

波がある場合は1〜65,535を表します。より大きな値や負数は算術で組み立てます。単項表記によるソースの長さは、この記号集合の意図した負担です。コンパクトな数値表記はツールや将来のバイナリー生成物で扱い、.dicks ファイル内に実行可能なテキストとして隠してはいけません。

値とスタックの順序​

基本型は次の4つです。

シグネチャのタグ型表現と規則
1Int符号付き64ビット整数。すべての算術結果を検査
2Bool厳密なtrue/false。数値への暗黙変換はしない
3TextUnicodeスカラーの列。UTF-8でリソースを計上し、孤立サロゲートは認めない
4Json境界で扱う不変のJSON値。暗黙の走査や型変換はしない

暗黙の型変換、万能の Any、null許容の基本型、基本命令の浮動小数点算術はありません。JSONではnullと有限の2進浮動小数点数を明示的に扱えます。基本のIntからJsonへの変換は、±9,007,199,254,740,991の外側の整数を拒否します。JSONのオブジェクトと配列は、境界で防御的に複製して凍結します。ホストのオブジェクトを指す生のポインターではありません。

スタックは底から頂上へ書き、右端を頂上とします。二項演算では b、a の順に取り出して a op b を計算します。特記しない限り、命令に波を付けると拒否します。省略可能な即値は n と表し、未指定なら記載したスタック上のオペランドを消費します。

命令表​

オペコードは、= の正確な個数です。

オペコード解読名波スタック・動作
1int省略可能[] → [Int]。未指定なら0、指定した場合はnを積む
2add省略可能[a,b] → [a+b]。即値形式は [a] → [a+n]
3emitなし[Int or Text] → []。出力項目を1つバッファへ追加
4sub省略可能[a,b] → [a-b] または [a] → [a-n]
5mul省略可能[a,b] → [a*b] または [a] → [a*n]
6div省略可能ゼロ方向へ切り捨てる整数除算
7rem省略可能被除数と同じ符号の剰余
8negなし[Int] → [Int]。検査付きの符号反転
9dupなし[a] → [a,a]。不変の値は保存領域を共有可能
10dropなし[a] → []
11swapなし[a,b] → [b,a]
12eq省略可能同じ基本型の [a,b] → [Bool]。即値の右辺はInt。Jsonは拒否
13lt省略可能Intの比較 [a,b] → [Bool] または [a] → [a<n]
14notなし[Bool] → [Bool]
15bool省略可能未指定ならfalse、波1個ならtrue。それより多い波は拒否
16textなし[] → [Text]。空のテキスト
17char省略可能[Text,Int] → [Text]。スカラー、または即値スカラーnを追加
18concatなし[Text,Text] → [Text]
19lengthなし[Text] → [Int]。書記素数やUTF-8バイト数ではなくUnicodeスカラー数
20ifなしBoolを消費し、then/elseブロックを実行
21elseなし省略可能な分岐の区切り
22endなしif、repeat、関数を閉じる
23repeat省略可能回数のIntを消費するかnを使う。本体はスタックの型を維持
24fn必須正の数値IDを持つモジュール直下の関数を開始
25inputs省略可能シグネチャの入力数。未指定なら0
26outputs省略可能シグネチャの出力数。未指定なら0
27bodyなしシグネチャを終え、実行する本体を開始
28call必須関数ID nを呼び出す。宣言した引数を消費し、宣言した結果を積む
29returnなし関数が宣言した出力スタックと完全に一致する値を返す
30local.get必須確実に初期化済みのローカル変数nを積む
31local.set必須値を取り出して関数内のローカルスロットn(1〜256)へ代入
32host必須宣言済みのホストABIインポートnを呼び出す
33jsonなし[primitive or Json] → [Json]。明示的な検査付き変換
34assertなしBoolを消費し、falseならトラップ
35int.textなし[Int] → [Text]。十進表記への変換

オーバーフローは桁あふれで折り返さず、トラップします。Int最小値の符号反転と MIN / -1 も含みます。検査付き除算の意味を揃えるため、MIN rem -1 もトラップします。ゼロによる除算・剰余、負の反復回数もトラップします。Unicodeのサロゲートコードポイントと、0〜0x10ffffの外側の値もトラップします。

関数と制御フロー​

暗黙のスクリプトは、fn で始まらない、空でない命令列です。関数1となり、引数を受け取らず、最終スタックから出力型を推論します。明示的な return や、内部の関数ヘッダーは含められません。

明示的なモジュールは、関数定義だけで構成します。

fn(id) inputs(count) int(type)... outputs(count) int(type)... body ... end

これは説明用の逆アセンブル表記で、実際のソースは引き続き記号トークンだけです。宣言する各型は、正の型タグを飛距離に持つ int トークンです。関数1がエントリーで、引数と結果を宣言できます。関数の重複やエントリーの欠落はエラーです。後で定義する関数の呼び出しにも対応します。0.2では直接・間接の再帰を拒否します。通常の終了スタックも早期returnのスタックも、出力シグネチャと完全に一致する必要があります。

関数はそれぞれ独立したフレームを持ちます。呼び出しはシグネチャの順に引数を消費し、シグネチャの順に結果を返します。ローカル変数の型は関数内で固定し、変更可能なグローバル変数はありません。ローカル変数を読むには、そこへ到達するすべての経路で代入済みでなければなりません。反復は0回の場合があるため、本体内の代入をループ後の確実な初期化とは扱いません。分岐ごとに異なる型を同じローカル変数へ代入すると、経路が排他的でも競合します。スロットの型は関数全体で1つです。

if の各分岐は、その後へ処理が続く経路で、同じスタック型を残す必要があります。returnする分岐は合流に参加しません。両方がreturnする場合、その後の命令は到達不能です。無条件returnの後の命令は拒否します。ループ本体からその後へ続く経路は、入力時のスタック型を保つ必要があります。本体内からのreturnは、ループを含む関数から戻ります。

未使用の定義も含め、すべての関数本体を検証します。コード生成とホストのインポートは、エントリーから静的に到達できる呼び出しグラフだけを対象にします。エフェクトと非同期性は間接的な呼び出しにも伝播します。機能の許可は、未使用の関数ではなくエントリーに対して検査します。ここでいう制御フローの到達可能性は構造的なもので、条件を定数として最適化することではありません。

上限​

コンパイル時のリソース上限
UTF-8のソースバイト数1,048,576
トークン数65,536
1トークンの波の数65,535
制御ブロックの入れ子64
1モジュールの関数数256
呼び出しグラフの深さ64
1フレームのスタックスロット数1,024
1関数のローカルID1..256
実行時のリソース既定の上限
fuel100,000
ホスト呼び出し数128
非同期ホスト呼び出しごとのタイムアウト5,000 ms
1つのTextのバイト数65,536
Text/Jsonの累積割り当て計上8,388,608 bytes
バッファ出力のバイト数65,536
JSONの深さ・走査項目数・直列化バイト数32 / 4,096 / 262,144

実行時オプションでは上限を下げられますが、上げられません。区切り以外の命令は、実行ごとにfuelを1単位消費します。反復は空のループも含め、繰り返すたびに追加で1単位消費します。シグネチャのマーカーと else / end はfuelを消費しません。fuelの計上に失敗した場合も、カウンターを増やしてからトラップします。値の境界検査では、最終出力も含め、文字列・JSONの検証用コピーを計上します。これは保守的な予算で、JavaScriptエンジンのヒープの測定ではありません。不変値の dup は値をコピーしません。

命令のfuelが制限するのは生成コードの実行で、信頼した同期ホスト内の処理時間ではありません。非同期タイムアウトはAbortSignalによる協調的なキャンセルで、停止したJavaScriptイベントループを中断したり、リモート書き込みのロールバックを保証したりすることはできません。ホスト操作を自動再試行することはありません。キャンセルは、命令の間と非同期ホストの完了を待つ間に検査します。

診断​

言語の診断は、安定した code、メッセージ、ソースの span を持ちます。範囲は0始まりのオフセット・長さと、1始まりの行・列です。有効なソースはASCIIなので、バイトオフセットとコード単位のオフセットが一致します。Rustのテキスト表示では、ソースの抜粋を100文字に制限します。

  • D1000..D1008:字句の上限、綴り、飛距離の規則。
  • D1100..D1109:文法、シグネチャ、ブロック、モジュールの構造。
  • D1110..D1112:名目的な型宣言と網羅的なResultの分岐。
  • D2020..D2022:構造化された型とレコードのフィールドのエラー。
  • D3020..D3023:コレクションの範囲、Resultの種類、明示的な変換、サイズ。
  • D2000..D2017:再帰、スタック・型、到達可能性、ローカル変数、インポート、エフェクト。
  • D2100:Rustのホスト宣言の検証。
  • D3000..D3018:機能の許可、キャンセル・fuel、オーバーフロー・Unicode、値の上限、ホスト・境界のエラー、アサーション。
  • D9000:コンパイラの不変条件の違反。コンパイラの不具合として報告してください。

TypeScript参照実装では、不正なホストメタデータに対してD2100ではなくTypeErrorを投げます。CLIのファイル・エンコード・引数のエラーは、言語の診断コードの範囲外です。実行時の予期しないホストエラーは内容を秘匿し、ホストの例外文をそのまま言語の診断へコピーしません。位置のメタデータにはホストの引数や秘密情報を含めません。