diff --git a/docs/development/roadmap/phases/phase-25.4-naming-cli-cleanup/README.md b/docs/development/roadmap/phases/phase-25.4-naming-cli-cleanup/README.md index 874acb1b..ee5f050b 100644 --- a/docs/development/roadmap/phases/phase-25.4-naming-cli-cleanup/README.md +++ b/docs/development/roadmap/phases/phase-25.4-naming-cli-cleanup/README.md @@ -1,55 +1,147 @@ -# Phase 25.4 — Naming & Stage‑1 CLI Cleanup +# Phase 25.4 — NamingBox SSOT & CLI 設定箱化 -Status: design only(小さな箱の片付けフェーズ) +**目的**: static box / global 呼び出しの名前決定を一元化し、Stage-1 CLI の環境変数処理を Config 箱に集約する。 -目的: -- static box / global 呼び出しまわりの命名規約を NamingBox に一本化し、レガシーなフォールバック経路を整理する。 -- Stage‑1 CLI の env/トグル解釈を 1 箇所の「設定箱」にまとめ、stage1_main の責務を薄くする。 -- MIR デバッグログ(`__mir__.log`)のタグと配置をドキュメントで見える化し、将来の正式機能化に備える。 +--- -## 1. NamingBox SSOT 化(static box naming) +## Task A: NamingBox SSOT化(✅ 完了) -- 対象: - - `src/mir/naming.rs` - - `src/mir/builder/decls.rs`(`build_static_main_box` など) - - `src/backend/mir_interpreter/handlers/calls/global.rs` -- ゴール: - - static box 名に触るコードはすべて NamingBox (`canonical_box_name`, `encode_static_method`, `normalize_static_global_name`) 経由に揃える。 - - VM 側の global 呼び出しは canonical 名(例: `Main._nop/0`)のみを見る。レガシーな「元名での再探索」フォールバックは撤去済みの状態を維持。 - - テスト: `src/tests/mir_static_box_naming.rs` を維持し、`apps/tests/minimal_to_i64_void.hako` が Main._nop/0 → StringHelpers.to_i64 経由で実行されることを固定。 +**Rust側統一**: +- `src/mir/naming.rs` を SSOT に設定 +- Builder/VM/Entry選択ロジックを統一 -## 2. Stage1 CLI 設定箱(env→Config 変換) +**Python側ミラー**: +- `src/llvm_py/naming_helper.py` 作成 +- Rust NamingBox と完全同一の意味論 -- 対象: - - `lang/src/runner/stage1_cli.hako` - - 新規: `Stage1CliConfigBox`(案) -- ゴール: - - NYASH_USE_STAGE1_CLI / STAGE1_EMIT_* / STAGE1_BACKEND / STAGE1_SOURCE / STAGE1_PROGRAM_JSON / NYASH_TO_I64_* などの env を、1 箇所で構造体 `Config` に変換する箱を作る。 - - stage1_main(args) は、この `Config` に対する分岐ロジックだけを持つ(env.get を散在させない)。 - - env の一覧と既定値/優先順位を docs(Phase 25.x or env-var reduction report)に反映し、将来の環境変数削減フェーズの入力にする。 +**完了コミット**: fa9cea51 (Rust), bceb20ed (Entry選択), 419214a5 (Python) -## 3. MIR ログ観測リスト(__mir__.log の整理) +--- -- 対象: - - `__mir__.log` を使用している .hako ファイル(例: `lang/src/runner/stage1_cli.hako`, `lang/src/shared/common/string_helpers.hako`) - - docs 新規: `docs/development/architecture/mir-logs-observability.md`(案) -- ゴール: - - どのタグ(例: `[stage1_main] ...`, `[string_helpers/to_i64] ...`)がどのレイヤ/目的のためのデバッグかを一覧にする。 - - dev 用ログと将来残したい観測ログを分けておき、Phase 25.x 以降で `__mir__` を正式な「MIR 観測 API」として設計するための下地にする。 +## Task B: Stage-1 CLI 設定箱(進行中) -## 4. Fixture の最小化(optional / 後続タスク) +### 目的 +`stage1_main` から env/トグル処理を分離し、1箇所の「設定箱」で env を解釈する。 -- 対象: - - `apps/tests/minimal_to_i64_void.hako` - - StringHelpers 系のテスト用 .hako(将来新設) -- ゴール: - - Void → 数値変換など、特定の型崩れバグを再現するための fixture を「本当に必要な最小の箱」に分解する。 - - 代表テストの MIR ログが過度に肥大化しないようにしつつ、numeric core / to_i64 の仕様を小さな .hako で固定していく。 +### Config フィールド設計 -## 5. 他フェーズとの関係 +#### **実行制御フィールド**(本番用) +```nyash +static box Stage1CliConfigBox { + // 1. Mode selection + mode: String // "emit_program_json" | "emit_mir_json" | "run" | "disabled" -- Phase 21.7(Normalization / Methodize Static Boxes): - - NamingBox による static 名正規化(`main` → `Main`)はすでに導入済みで、本フェーズはその SSOT 化と VM 側フォールバックの整理にあたる。 - - 実際の「Global(\"Box.method\") → Method{receiver=static singleton}」リライトは Phase 21.7 本体で実装する予定。 -- Phase 25.1(Stage‑1 UsingResolver / CLI 本線): - - 本フェーズは 25.1 の「片付け+観測」側タスクとして扱い、CLI 本体や UsingResolver の仕様変更は行わない(構造整理のみ)。 + // 2. Backend selection + backend: String // "vm" | "llvm" | "pyvm" (default: "vm") + + // 3. Input sources + source_path: String // STAGE1_SOURCE: .hako file path + source_text: String // STAGE1_SOURCE_TEXT: direct source text (test use) + program_json_path: String // STAGE1_PROGRAM_JSON: pre-built Program JSON path + + // 4. Control flags + use_stage1_cli: Integer // NYASH_USE_STAGE1_CLI: 1=enable, 0=disable +} +``` + +#### **Dev専用フラグ**(開発用) +```nyash +static box Stage1CliConfigBox { + // Debug & dev toggles + debug: Integer // STAGE1_CLI_DEBUG: 1=verbose logging + to_i64_force_zero: Integer // NYASH_TO_I64_FORCE_ZERO: 1=force 0 on Void (dev workaround) +} +``` + +### 環境変数マッピング + +| 環境変数 | Config フィールド | 用途 | デフォルト | +|---------|------------------|-----|----------| +| `NYASH_USE_STAGE1_CLI` | `use_stage1_cli` | CLI有効化 | 0 (無効) | +| `STAGE1_EMIT_PROGRAM_JSON` | `mode` | "emit_program_json" | - | +| `STAGE1_EMIT_MIR_JSON` | `mode` | "emit_mir_json" | - | +| `STAGE1_BACKEND` | `backend` | backend選択 | "vm" | +| `STAGE1_SOURCE` | `source_path` | .hakoパス | - | +| `STAGE1_SOURCE_TEXT` | `source_text` | 直接ソース(テスト用) | - | +| `STAGE1_PROGRAM_JSON` | `program_json_path` | Program JSONパス | - | +| `STAGE1_CLI_DEBUG` | `debug` | デバッグログ | 0 (無効) | +| `NYASH_TO_I64_FORCE_ZERO` | `to_i64_force_zero` | Void→0強制化(dev用) | 0 (無効) | + +### Mode 決定ロジック + +```nyash +method from_env() { + local cfg = new MapBox() + + // Mode selection (排他的) + if env.get("STAGE1_EMIT_PROGRAM_JSON") == "1" { + cfg.set("mode", "emit_program_json") + } else if env.get("STAGE1_EMIT_MIR_JSON") == "1" { + cfg.set("mode", "emit_mir_json") + } else if env.get("NYASH_USE_STAGE1_CLI") == "1" { + cfg.set("mode", "run") + } else { + cfg.set("mode", "disabled") + } + + // Backend + local b = env.get("STAGE1_BACKEND") + if b == null { b = "vm" } + cfg.set("backend", "" + b) + + // Sources + cfg.set("source_path", env.get("STAGE1_SOURCE")) + cfg.set("source_text", env.get("STAGE1_SOURCE_TEXT")) + cfg.set("program_json_path", env.get("STAGE1_PROGRAM_JSON")) + + // Flags + cfg.set("debug", env.get("STAGE1_CLI_DEBUG")) + cfg.set("to_i64_force_zero", env.get("NYASH_TO_I64_FORCE_ZERO")) + + return cfg +} +``` + +### 内部専用環境変数(Config 外) + +以下は **Config に入れない**(内部処理・参照のみ): + +- `HAKO_STAGEB_APPLY_USINGS` - Stage-B using 適用制御(BuildBox内部) +- `HAKO_MIR_BUILDER_DELEGATE` - MirBuilder delegate トグル(MirBuilderBox内部) +- `NYASH_SCRIPT_ARGS_JSON` - スクリプト引数(`env.set`で設定、読み取りは Stage0) + +--- + +## Task C: MIR ログ観測リスト(後続) + +`__mir__.log` のタグと用途を一覧化し、dev専用/観測用を分類する。 + +詳細: 後続セクション参照 + +--- + +## 実装計画 + +### B-1. 設計(✅ 完了) +- Config フィールド設計: 上記 + +### B-2. Config 箱実装(次) +- `Stage1CliConfigBox` 作成 +- `from_env()` メソッド実装 + +### B-3. stage1_main リファクタ(次) +- `local cfg = Stage1CliConfigBox.from_env()` を先頭で呼び出し +- `env.get` 呼び出しを `cfg.get` に置換 + +### B-4. テスト(次) +- `cargo test mir_stage1_cli_stage1_main_shape_verifies` +- `cargo test mir_stage1_cli_entry_like_pattern_verifies` +- `tools/stage1_debug.sh` 手動実行確認 + +--- + +## 参考 + +- Phase 25.4-A 完了コミット: fa9cea51, bceb20ed, 419214a5 +- Stage-1 CLI 本体: `lang/src/runner/stage1_cli.hako` +- Stage-1 テスト: `src/tests/stage1_cli_entry_ssa_smoke.rs` diff --git a/lang/src/runner/stage1_cli.hako b/lang/src/runner/stage1_cli.hako index 19c471e5..1fb65cee 100644 --- a/lang/src/runner/stage1_cli.hako +++ b/lang/src/runner/stage1_cli.hako @@ -12,6 +12,41 @@ using lang.compiler.entry.using_resolver_box as Stage1UsingResolverBox using lang.mir.builder.MirBuilderBox using selfhost.shared.host_bridge.codegen_bridge as CodegenBridgeBox +// Stage-1 CLI Config Box - Environment variable parsing and configuration management +static box Stage1CliConfigBox { + // Parse all env vars into a Config map (MapBox) + method from_env() { + local cfg = new MapBox() + + // Mode selection (mutually exclusive) + if env.get("STAGE1_EMIT_PROGRAM_JSON") == "1" { + cfg.set("mode", "emit_program_json") + } else if env.get("STAGE1_EMIT_MIR_JSON") == "1" { + cfg.set("mode", "emit_mir_json") + } else if env.get("NYASH_USE_STAGE1_CLI") == "1" { + cfg.set("mode", "run") + } else { + cfg.set("mode", "disabled") + } + + // Backend (default: "vm") + local b = env.get("STAGE1_BACKEND") + if b == null { b = "vm" } + cfg.set("backend", "" + b) + + // Input sources + cfg.set("source_path", env.get("STAGE1_SOURCE")) + cfg.set("source_text", env.get("STAGE1_SOURCE_TEXT")) + cfg.set("program_json_path", env.get("STAGE1_PROGRAM_JSON")) + + // Dev flags + cfg.set("debug", env.get("STAGE1_CLI_DEBUG")) + cfg.set("to_i64_force_zero", env.get("NYASH_TO_I64_FORCE_ZERO")) + + return cfg + } +} + static box Stage1Cli { // Source(.hako) → Program(JSON v0) method emit_program_json(source) { @@ -105,49 +140,56 @@ static box Stage1Cli { return 0 } - // CLI dispatcher (stub) + // CLI dispatcher (Config box refactored) method stage1_main(cli_args_raw) { // Fail-fast: Work around MIR Builder type confusion by avoiding parameter usage // The parameter cli_args_raw is mistyped as ParserBox, so we ignore it entirely // and create a fresh ArrayBox from environment variables instead local args_safe = new ArrayBox() - // Log entry point args for undefined value debugging(argv 自体は本線ロジックでは使わない) - if env.get("STAGE1_CLI_DEBUG") == "1" { + // Config box: Parse all env vars into structured config + local cfg = Stage1CliConfigBox.from_env() + local mode = cfg.get("mode") + local debug = cfg.get("debug") + + // Log entry point for debugging + if debug == "1" { __mir__.log("[stage1_main] args_safe at entry", args_safe) - print("[stage1-cli/debug] stage1_main ENTRY: env_emits={prog=" + ("" + env.get("STAGE1_EMIT_PROGRAM_JSON")) + ",mir=" + ("" + env.get("STAGE1_EMIT_MIR_JSON")) + "} backend=" + ("" + env.get("STAGE1_BACKEND"))) + print("[stage1-cli/debug] stage1_main ENTRY: mode=" + ("" + mode) + " backend=" + ("" + cfg.get("backend"))) } - { - local use_cli = env.get("NYASH_USE_STAGE1_CLI") - if use_cli == null || ("" + use_cli) != "1" { - if env.get("STAGE1_CLI_DEBUG") == "1" { - print("[stage1-cli/debug] stage1_main: NYASH_USE_STAGE1_CLI not set, returning 97") - } - return 97 + + // Early exit if CLI is disabled + if mode == "disabled" { + if debug == "1" { + print("[stage1-cli/debug] stage1_main: mode=disabled, returning 97") } + return 97 } - // Log env toggles before emit_prog branch - if env.get("STAGE1_CLI_DEBUG") == "1" { - __mir__.log("[stage1_main] env toggles", - env.get("STAGE1_EMIT_PROGRAM_JSON"), - env.get("STAGE1_EMIT_MIR_JSON"), - env.get("STAGE1_BACKEND"), - env.get("STAGE1_SOURCE"), - env.get("STAGE1_PROGRAM_JSON")) + // Log config toggles before dispatch + if debug == "1" { + __mir__.log("[stage1_main] config", + cfg.get("mode"), + cfg.get("backend"), + cfg.get("source_path"), + cfg.get("program_json_path")) } - // Prefer env-provided mode/source to avoid argv依存の不定値 - local emit_prog = env.get("STAGE1_EMIT_PROGRAM_JSON") - local emit_mir = env.get("STAGE1_EMIT_MIR_JSON") - local backend = env.get("STAGE1_BACKEND"); if backend == null { backend = "vm" } - local source = env.get("STAGE1_SOURCE") - local prog_path = env.get("STAGE1_PROGRAM_JSON") + // Extract config fields + local source = cfg.get("source_path") + local source_text = cfg.get("source_text") + local prog_path = cfg.get("program_json_path") + local backend = cfg.get("backend") - if emit_prog == "1" { + // Dispatch by mode + if mode == "emit_program_json" { if source == null || source == "" { - print("[stage1-cli] emit program-json: STAGE1_SOURCE is required") - return 96 + if source_text != null && source_text != "" { + source = source_text // Fallback to direct text for testing + } else { + print("[stage1-cli] emit program-json: STAGE1_SOURCE is required") + return 96 + } } local ps = me.emit_program_json(source) if ps == null { return 96 } @@ -155,7 +197,7 @@ static box Stage1Cli { return 0 } - if emit_mir == "1" { + if mode == "emit_mir_json" { local prog_json = null if prog_path != null && prog_path != "" { prog_json = me._read_file("[stage1-cli] emit mir-json", prog_path) @@ -173,7 +215,7 @@ static box Stage1Cli { return 0 } - // Default: run path (env-provided backend/source only) + // Default: run path (mode == "run") if source == null || source == "" { print("[stage1-cli] run: source path is required (set STAGE1_SOURCE)") return 96