feat(stage1-cli): Config箱化完了 - env処理を一元管理

Phase 25.4-B: Stage-1 CLI 設定箱実装

## 📦 実装内容

### 1. Config 箱作成
- 新規: `Stage1CliConfigBox` (`lang/src/runner/stage1_cli.hako:16-48`)
- `from_env()` メソッドで全環境変数を MapBox に集約
- Mode/Backend/Sources/Dev flags を構造化

### 2. stage1_main リファクタ
- `local cfg = Stage1CliConfigBox.from_env()` で設定取得
- `env.get` 散在 → `cfg.get` 一箇所参照に統一
- Mode-based dispatch (emit_program_json | emit_mir_json | run | disabled)

### 3. 環境変数マッピング

**実行制御**:
- `NYASH_USE_STAGE1_CLI` → mode="run"
- `STAGE1_EMIT_PROGRAM_JSON` → mode="emit_program_json"
- `STAGE1_EMIT_MIR_JSON` → mode="emit_mir_json"
- `STAGE1_BACKEND` → backend (default: "vm")
- `STAGE1_SOURCE` / `STAGE1_SOURCE_TEXT` / `STAGE1_PROGRAM_JSON` → sources

**Dev専用**:
- `STAGE1_CLI_DEBUG` → debug
- `NYASH_TO_I64_FORCE_ZERO` → to_i64_force_zero

### 4. 設計文書
- 新規作成: `docs/development/roadmap/phases/phase-25.4-naming-cli-cleanup/README.md`
- Config フィールド設計・環境変数マッピング完全文書化

## 技術的成果
- **env.get 削減**: stage1_main 内 15箇所 → 1箇所(Config生成)に集約
- **Mode決定ロジック**: 排他的Mode選択を Config 箱に集中
- **保守性向上**: 環境変数追加時は Config 箱のみ修正

## テスト結果
 cargo test mir_stage1_cli_stage1_main_shape_verifies: 1 passed
 cargo test mir_stage1_cli_entry_like_pattern_verifies: 1 passed
 cargo test mir_static_box_naming: 2 passed
 Background stage1_cli execution: RC=0

## 参考
- Phase 25.4-A: fa9cea51, bceb20ed, 419214a5
- Config 設計: docs/development/roadmap/phases/phase-25.4-naming-cli-cleanup/README.md

🎉 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-21 09:43:42 +09:00
parent 419214a5a9
commit 554ba96f76
2 changed files with 207 additions and 73 deletions

View File

@ -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 debuggingargv 自体は本線ロジックでは使わない)
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