Files
hakorune/docs/development/roadmap/phases/phase-17/module-interfaces.md

4.6 KiB
Raw Blame History

Phase 17 接続モデル(最小): JSON NDJSON over stdio, exe↔exe

目的: まずは「簡単・見える・壊れない」。単一の接続モデルJSONを1行ずつ、標準入出力でやり取りで、エンジンを別プロセスの小さな実行ファイルとして積み上げる。

方針

  • 1接続モデルのみ: NDJSON1メッセージ=1行のJSON
  • 1トランスポートのみ: 標準入出力(子プロセス起動)。パイプ/ソケットは後回し。
  • 1エンジン=1 exe: nyash-engine-core13, nyash-engine-loopなど。役割ごとに小さく分割。
  • 可視化容易: すべてのトレースをNDJSONで出力し、そのまま保存・比較・変換jq/簡易スクリプト)。

メッセージ共通フィールド

  • op: 操作名(例: "load_module", "call", "trace_sub"
  • id: リクエストID数値/文字列)。応答に同じidを返す。
  • schema: プロトコル版(整数, 例: 1
  • ts: 送信者時刻(オプション, ns/μs/ms表現は自由

応答の共通:

  • ok: true/false
  • id: リクエストと同一
  • err: 失敗時のみ {code, message, data?}

操作一覧(最小)

  1. load_module
  • 要求: {op:"load_module", id:1, schema:1, ir:"core13|loop", format:"json|bin", bytes:"<base64>"}
  • 応答: {ok:true, id:1, module_id:1, features:["interp","trace"], ir:"core13"}
  1. call
  • 要求: {op:"call", id:2, module_id:1, func:"main", args:[1,2,3], timeout_ms:5000?}
  • 応答: {ok:true, id:2, value:3, events_dropped:0}
  1. trace_subトレース購読
  • 要求: {op:"trace_sub", id:3, mask:["EnterFunc","ExitFunc","LoopIter","Branch","ExternCall"], flush_ms:50?}
  • 応答: {ok:true, id:3}
  • イベントストリーム: 別行で {"event":"EnterFunc", "func":"main", ...} を逐次出力(応答行とは独立)
  1. unload任意
  • 要求: {op:"unload", id:4, module_id:1}
  • 応答: {ok:true, id:4}
  1. pingヘルスチェック
  • 要求: {op:"ping", id:5}
  • 応答: {ok:true, id:5, now: 1725600000}

エラー例:

  • 応答: {ok:false, id:2, err:{code:"E_NO_FUNC", message:"function not found", data:{func:"main"}}}

トレースイベントNDJSON

  • 例: {"event":"EnterFunc","func":"main","args":[1],"ts":...}
  • 最小セット: EnterFunc, ExitFunc, Block, PhiMerge, LoopIter, Branch, ExternCall, Safepoint, Barrier
  • フィールドの原則: event必須、その他は柔軟に拡張し未知キーは無視可能。

エンジンexeの約束

  • 起動直後にバナー等を出力しない(標準出力は完全にプロトコル専用)。
  • 標準エラーは自由(ログ用)。
  • 読み込みは行単位でJSONをパース、応答は必ず1行。
  • イベントは応答と独立行で流してよいtrace_sub済みのとき
  • 大きなIRバイナリは bytes を base64。JSON IRはそのまま文字列可。

クライアント側nyash-cli

  • nyash run --engine=remote --exe ./nyash-engine-core13 --ir=core13 -- program.ny
  • 実装は単純: 子プロセスspawn → stdin/stdoutにNDJSON → load_modulecall
  • --tracetrace_sub を投げてNDJSONログをファイル保存そのまま可視化可能

可視化(最小)

  • NDJSON→Markdown表: jq -r で列抽出, |区切りでテーブル化。
  • NDJSON→CSV: jq -r '[.event, .func, .block, .ts] | @csv'
  • 差分: 2つのトレースを event+順序でzip比較。簡易Python/nyashツールは後続で追加可。

バージョニングと互換

  • schema は整数で宣言、互換性は「追加のみ」原則。意味変更や削除は schema+1
  • 不明キーは無視。必須キー欠落は E_SCHEMA

タイムアウト/健全性

  • call.timeout_ms を受付。内部でキャンセル/中断可能な範囲で対応。
  • ハートビート: ping を定期送信して固まり検出。

セキュリティ

  • ローカル実行のみを想定(最小)。ネットワーク露出はしない。
  • 外部からのファイルアクセス/実行はプロトコルに含めないエンジンはIR実行に限定

最小試験手順(手動)

  1. エンジンを起動(手動で別ターミナル)。catでNDJSONを流せるならなお良い。
  2. load_module を送信小さなIR
  3. trace_sub を送信。
  4. call を送信 → 応答とイベント列を保存。
  5. jqでCSV化して目視。

将来拡張(後回し)

  • 長期接続(ソケット/名前付きパイプ)
  • バイナリMessagePack/bincode実装速度重視
  • 圧縮gzip
  • ストリーム再同期(途中から読み直し)