WebAssembly にコンパイルする言語 msaw を作りました。minimini_lang の続きで、関数・分岐・ループ・ローカル変数・ホスト関数の import を備えています。Wasm 向け言語開発のテストを兼ねていて、今後作る言語のベースにする予定です。
msaw Playground でブラウザから試せます。
import env.print_i32 as fn print_i32(i32);
fn main() {
print_i32(_gcd(-12, 18)); // 6
}
fn _gcd(a: i32, b: i32) -> i32 {
if a < 0 { a = -a; }
if b < 0 { b = -b; }
loop {
if b == 0 {
return a;
}
let r = a % b;
a = b;
b = r;
}
}
; で終わる。改行に意味はない//[A-Za-z_][A-Za-z0-9_]*"..."。エスケープなしi32 のみ。比較の結果は 1(真) / 0(偽)、if の条件は 0 以外が真。
import env.print_i32 as fn print_i32(i32); // 関数の import
import env.buffer as memory(1); // メモリの import
global let start = import env.start_string as i32; // グローバルの import
global let len = 12; // グローバル変数
data(start, "hello world!"); // メモリの初期値
fn main() { ... } // 関数定義
定義の順番は自由です。
モジュール名.名前 の2段。関数の引数は型のみ書く。memory(n) の n は最小ページ数mut キーワードはない)。import したグローバルには代入できない_ で始まる関数は非公開、それ以外は export される。引数には代入できるfn add(a: i32, b: i32) -> i32 { return a + b; } // export される
fn _helper() { } // export されない
| 文 | 意味 |
|---|---|
let x = 式; | ローカル変数の宣言 (初期値必須) |
x = 式; | 代入 |
式; | 評価して値は捨てる |
if 条件 { } else if 条件 { } else { } | 分岐 |
loop { } | 無限ループ |
break; / continue; | 内側の loop を抜ける / 先頭へ戻る |
return 式; / return; | 関数から戻る |
x = y = 1; や f(x = 1); は書けないif と loop の本体は { } 必須while / for はない| 優先順位 | 演算子 | 結合 |
|---|---|---|
| 1 | -x | 右 |
| 2 | * / % | 左 |
| 3 | + - | 左 |
| 4 | == != < <= > >= | 連鎖不可 |
/ は 0 方向へ切り捨て、% の符号は左辺に従う。0 除算はトラップa < b < c は構文エラー&& || ! は未実装関数呼び出しの引数の数、戻り値のない関数を値として使うことはコンパイル時にチェックされます。
let の変数は宣言した { } の終わりまで有効です。同じ名前で let するとシャドーイングになります。
let x = 1;
let x = x + 1; // 右辺の x は 1
if x > 0 {
let x = x * 10; // if の中だけの x (20)
}
// ここの x は 2
戻り値のある関数で、末尾まで処理が流れ落ちる可能性があるとコンパイルエラーになります。
fn f() -> i32 { if 1 { return 1; } }
// error: `f` may reach its end without returning a value
break のない loop は抜けないものとして扱います。冒頭の _gcd が末尾に return なしで通るのはこのためです。
ホストから env として以下を import できます。
| import | 型 | 内容 |
|---|---|---|
env.print_i32 | fn(i32) | 数値を出力 |
env.print_string | fn(i32) | start_string から n バイトを文字列として出力 |
env.buffer | memory(1) | 共有メモリ |
env.start_string | i32 | 文字列の置き場所 (100) |
import env.print_string as fn print_string(i32);
import env.buffer as memory(1);
global let start_string = import env.start_string as i32;
data(start_string, "hello world!");
fn helloworld() {
print_string(12);
}
プレイグラウンドでは関数と引数を指定して実行します。無限ループは停止ボタンで止められます。