REPL 仕様
本書は hikari を引数なしで起動したときの REPL の仕様を定める。起動ディスパッチ全体は hikari-command.md、言語の意味論は language-spec.md、組込関数群は prelude.md を参照。
引数なしで起動。標準入力から 1 行ずつ式を読み、評価結果を表示する。
$ hikari hikari>
動作モード
stdin / stdout の両方が端末 (TTY) に接続されている場合は interactive モード (行編集 + ヒストリ + 複数行継続 + ライブ syntax highlight)、それ以外 (パイプ・リダイレクト・テスト) は batch モード (1 行ずつ読むだけのループ) で動く。出力内容はどちらも同等。interactive モードの着色は入力中のソースをトークン単位で色付けし、行がパース可能なときは識別子の意味分類 (slot/local/inner/builtin/type, LSP と同一の highlight パッケージ由来) も反映する。環境変数 NO_COLOR が設定されている場合は着色しない。
プロンプト
| 状況 | プロンプト |
|---|---|
| 通常入力 | hikari> |
| 文・式が途中 (継続) | ...> |
「途中」の判定は括弧/ブレース/角括弧のネスト深さで決定する。
interactive モードでは、構文的に閉じている行でも Shift+Enter / Alt+Enter で確定せず
任意位置に改行を挿入できる (continue プロンプト ...> に移る)。Shift+Enter は端末が
kitty keyboard protocol に対応している場合に有効で、Alt+Enter は対応有無に関わらず効く。
パーサーモード
REPL の入力は平坦な宣言と文の列として解釈される。綴りはファイルのトップレベルと同じだが (language-spec.md §13.1)、束縛の行き先が違う — ファイルではトップレベル束縛がファイルオブジェクトのスロットになるのに対し、REPL ではセッション env の束縛になる (下記「評価ループ」)。
この平坦な読み方は REPL のほか、文字列補間サブパース (language-spec.md §7.2.1) と、本書「実装構成 (self-host)」のコア自身 (repl_core.hika) の読み込みで共用される。コアは入口が REPL セッションへ流し込んで評価するので、1 入力と同じ道を通る。
処理系が同梱する他の self-host ソース (prelude と標準ライブラリの .hika) はこの道を通らない。あちらは普通のファイルとして読まれ (1 領域の slot-list。language-spec.md §13.1)、top-level 束縛はファイルオブジェクトのメンバーになる。読む道は VM と AOT で 1 本である。
仮想ファイル名は <repl> で、エラーメッセージの Position 表記にもこの名前が現れる。
評価ループ
- 1 行入力を読む。空行は読み飛ばす。
- 継続が必要な場合は
...>を出してさらに読み、入力に結合する。 - パース。失敗時はエラーを表示してループ継続。
- 評価。
Error値を返したらそれを表示して継続。 - 結果が
()(unit) でも束縛文 (x := ...) でもなければ結果の inspect 表記を出力。
env は REPL セッション全体で共有される (前の行で x := 10 した後、次の行で x を参照できる)。
セッション束縛と再宣言
セッションの束縛表は名前とその可変性を入力をまたいで保つ。x := 1 は immutable な束縛を、mutable x := 1 は mutable な束縛を作る (language-spec.md §6.1)。入力の境界はスコープの境界ではない — セッションの top-level は最初から最後まで 1 つのスコープである。
したがってファイルと同じ規則が入力をまたいでも当たる:
| 先の入力 | 続く入力 | 結果 |
|---|---|---|
x := 1 |
x := 2 |
静的エラー already-declared (同一スコープの再宣言) |
x := 1 |
x = 2 |
静的エラー immutable-assign (immutable への代入) |
mutable x := 1 |
x := 2 |
静的エラー already-declared (同一スコープの再宣言) |
mutable x := 1 |
x = 2 |
更新 (mutable の再代入。型は確立時のまま。static-analysis.md §2.2) |
| (宣言なし) | x = 2 |
エラー undefined (= は名前を導入しない。language-spec.md §6.1) — ただし名乗るのは実行時である (下の「未定義名は実行時に名乗る」) |
打ち直しのために immutable を再宣言する余地は無い — 別の名前を選ぶか、初めから mutable で宣言する。REPL の便宜のためにここを緩めると、x := 1 の後の x = 2 が「ファイルでは静的エラー・REPL では通る」という二重の規則になる。同じ入力を .hika へ貼り戻して動かない形を REPL で書けるようにはしない。
規則は静的検査が担い (「静的型検査」)、それを通さず評価だけを走らせた場合の防波堤を実行系が持つ。
セッションが保つのは値の束縛だけではない。同じスコープに属するものはすべて入力をまたいで生きる — 型宣言 (type T := Int)・enum 宣言 (enum C := OneOf(Red, Green))・分解束縛が導入した名前 ({ a, b } := o)・宣言した関数型の契約 (f: { Int | Int } := …) も、次の入力から同じように見える。したがって上の表は宣言の形を問わず一様に当たる ({ a, b } := o を 2 度書けば a と b がそれぞれ already-declared になる)。
終了
Ctrl+D (EOF) または Ctrl+C で終了する。interactive モードでは raw mode 解除を defer で保証する。
ブロッキング I/O (input) と Future
input 等のブロッキング I/O は呼び出し時点で Future を返す (prelude.md §1 / §9.4)。REPL での扱い:
!で待った場合 (例:input("name? ")!、x := input("name? ")!) は読み取り完了まで待ち、解決値 (Result(String)) を返す / 束縛する。!を当てずに評価した場合は結果のFutureを inspect 表記 (未解決なら<future pending>) として表示する。値は束縛されない。最後まで必要な値はf!で明示的に待つこと (language-spec.md §13.3)。
interactive モードでは対話端末の行読み取りを直列化し、input の読み取りと次プロンプトの読み取りが同時に走らないようにする。これにより ! を付け忘れても REPL は固まらない。! を当てなかった input はその行の評価後に読み取りを完了させ (プロンプトを出して 1 行受け取り)、結果を破棄してから次プロンプトを出す。
静的型検査
各入力行はパース成功後・評価前に静的型検査 (static-analysis.md §2) を受け、型エラーが 1 件でもあればその診断を表示して その行を評価せず 次プロンプトに進む。型検査がクリーンな行は通常どおり評価する。
検査は「評価ループ」の 3 (パース) と 4 (評価) の間に挟まる。診断の文言・位置表記は hikari-command.md §5.5 / check.md と同じ (<repl>:<line>:<col>: <msg>、interactive モードではエラー色で着色)。
検査は 1 入力単位で行うため、前の入力で束縛した名前の宣言結果型は当該入力の検査からは参照できない。「名前付き関数のパラメーター型必須化」のような入力内で完結する検査は効くが、型の越境照合は対象外になる (健全側=誤検出は出さない)。
越境するのはセッション束縛の名前と可変性だけである。検査はセッションの束縛表をトップレベルスコープの既存束縛として受け取るので、「セッション束縛と再宣言」の already-declared / immutable-assign は入力をまたいでも当たる。渡すのが名前と可変性に限られるため、型についての誤検出はこれによって増えない。
未定義名は実行時に名乗る
未定義の名前 (nosuch(1) のような値位置の参照と、x = 2 のような束縛の無い代入) は、1 入力ずつの検査では断定できないものとして静的検査から落とす。名乗るのは評価の側で、診断は undefined: nosuch のようにその名前を名乗る。ファイルを検査する経路では同じエラーが静的に当たるので、違うのは名乗る時点だけで、通る形が増えるわけではない。
検査は常に有効で、セッション途中での切り替えはできない (無効化する手段は無い)。REPL 入力は header を要求しない平坦な文列 (「パーサーモード」) である。
実装構成 (self-host)
REPL のポリシー層 — 1 入力の「パース → 型検査 → 評価 → 表示判定」と、batch モードのループ本体 — は Hikari 自身で記述し (repl_core.hika、コンパイル時に取り込んで処理系に同梱)、std/hikari/repl.md の std:hikari/repl の上で動く。コアは 2 層から成る:
handle_line— 1 入力をSessionで評価し、本書「評価ループ」3–5 の規則 (パースエラー表示・型検査の診断・unit / 束縛文の非表示) を適用する。3 フロントエンドが共有する。run— プロンプト出力・継続入力 (needs_more) の結合・行読みを含む batch 用フルループ。
フロントエンドごとの分担:
| フロントエンド | ループ・行取得 | ポリシー層 |
|---|---|---|
| batch (パイプ・リダイレクト・テスト) | Hikari (run) |
Hikari |
| interactive (端末) | Rust — 行編集・履歴・複数行継続・ライブ着色 (rustyline) |
Hikari (handle_line) |
| web (ブラウザー REPL) | JS — 入力 UI・履歴 (repl.js) | Hikari (handle_line、wasm 経由) |
行編集と色付け (highlight) はフロントエンド責務であり、Hikari コアは装飾前の素のテキストだけを扱う。どのフロントエンドでも出力内容は同等 (「動作モード」)。