WASM にコンパイルする言語を作った話

https://github.com/KajizukaTaichi/mystia

# 動機
Rustの`wasm_bindgen`は色々と制約が多いのが課題で、あまり好きではなかった。
~~`wasm_bindgen`は本当に屑で糞です。少しの事やるのに死ぬほど冗長なコードになる~~
かといってAssemblyScriptやEmscriptenは環境構築が面倒で使う気にはなりません。

ということで自分好みのWebアセンブリ向け言語を作り始めた。
プロジェクト名は「**Mystia**」と言います（特に深い意味は無い）。

# 特徴

### 静的型付け
コンパイル時に厳密に型チェックされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/aa42769f-7846-4341-b7e4-90cb6f56397a.png)

`--summary`オプションで型推論の結果を表示できます。

### Rust/OCamlライクな文法
というかデザインは完全に私の思想になっています。
関数定義と代入は明示的に`let`で行い、最後の式は`return`無しでも値を返します。

![スクリーンショット 2025-05-20 16.26.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/1c469a80-ddc5-42f0-a5f4-f5a3bf2c660e.png)

# コンパイラ
LLVMは使わず、抽象構文木から直接WAT(WebAssembly Text Format)を生成します。
Rustで実装しましたが、いずれはセルフホスティングも出来るようにしたい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/ab8bd018-6b9c-46a0-aee6-014d28fc47b9.png)

コード量も割とコンパクトです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/794ef431-5fbc-4ea4-89cc-cb4ea40798c2.png)

# エラーメッセージ

![スクリーンショット 2025-05-20 12.11.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/bd24b0fd-9fb1-4e65-a045-e39864255a16.png)

# 他言語連携
JavaScriptとのFFI(Foreign Function Interface)も作りました。
型情報を元にWASMのメモリから値を読み込んでいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/12bdcbb4-e309-45d3-b124-0ccf30558d6e.png)

# REPL
対話実行環境はNode製です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/1a28aed2-d017-4642-b1c5-b3ef6e50dbcb.png)

# Playground
インストール/環境構築不要でブラウザ上で動かせます。
ぜひ試してみてください！

https://kajizukataichi.github.io/mystia/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/de0a4550-5a34-4650-ad78-edc966a33250.png)

# 実用性

アプリもちゃんと書けます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2952107/f7422639-59b2-4c5b-90ad-4bbdc6d27fbf.png)

```rust
type Elm = int;

load new_elm(tag: str, parent: Elm): Elm;
load upd_elm(id: Elm, prop: str, content: str): void;
load evt_elm(id: Elm, name: str, func: str): void;
load concat(a: str, b: str): str;
load to_str(n: num): str;

pub let model = @{
    title: "Counter App",
    count: 0,
    layout: @{
        panel: null
    }
};

let view() = {
    let formatted = "Number: " + (model.count as str);
    upd_elm(model.layout.panel, "innerHTML", formatted)
};

pub let inc_btn() = {
    let model.count + 1;
    view()
};

pub let inc_key(key_code: int) = {
    if key_code == 39 then {
        let model.count + 1
    } else if key_code == 37 then {
        let model.count - 1
    };
    view()
};

let app() = {
    let frame = new_elm("div", null);
    upd_elm(frame, "style", "margin: 100px");
    evt_elm(frame, "keydown", "inc_key");

    let title = new_elm("h1", frame);
    upd_elm(title, "innerHTML", model.title);

    let model.layout.panel = new_elm("p", frame);
    upd_elm(model.layout.panel, "style", "font-family: monospace");
    view();

    let btn = new_elm("button", frame);
    upd_elm(btn, "innerHTML", "Increment");
    upd_elm(btn, "className", "button");
    evt_elm(btn, "click", "inc_btn")
};

app();
model.title

```

# 目標

制約が少なく**自由度の高い**WASM向け言語の選択肢となる事を目指しています。
また、今年のU-22プログラミングコンテストに応募予定です。

最後までご覧頂きありがとうございました！