feat(stage1): Phase 25.1 - Stage1 CLI デバッグ改善 + 環境変数削減計画
- ✅ Stage1 CLI デバッグログ追加 - lang/src/runner/stage1_cli.hako: STAGE1_CLI_DEBUG対応 - 各関数でentry/exit/状態ログ出力 - SSAバグ調査を容易化 - ✅ Rust bridge 実装 - src/runner/stage1_bridge.rs: 子プロセス起動・環境変数配線 - NYASH_ENTRY設定、モジュールリスト生成 - ✅ デバッグヘルパースクリプト - tools/stage1_debug.sh: 環境変数自動診断・詳細ログ - tools/stage1_minimal.sh: 推奨5変数のみで実行 - ✅ 環境変数削減計画(25個→5個) - docs/development/proposals/env-var-reduction-report.md - 使用箇所マトリックス、依存関係グラフ - 6段階削減ロードマップ(80%削減目標) - docs/development/proposals/stage1-architecture-improvement.md - 他言語事例調査(Rust/Go/Nim) - アーキテクチャ統一案、実装ロードマップ - ✅ LoopForm v2 設計ドキュメント - docs/development/roadmap/phases/phase-25.1/stage1-usingresolver-loopform.md 🎯 成果: Stage1起動の複雑さを可視化、80%削減計画確立
This commit is contained in:
573
docs/development/proposals/env-var-reduction-report.md
Normal file
573
docs/development/proposals/env-var-reduction-report.md
Normal file
@ -0,0 +1,573 @@
|
||||
# 環境変数徹底調査レポート - 25個から5個への削減計画
|
||||
|
||||
**作成日**: 2025-11-21
|
||||
**目的**: セルフホスティング実装における環境変数の複雑さを根本解決
|
||||
|
||||
---
|
||||
|
||||
## 📊 使用箇所マトリックス
|
||||
|
||||
| 環境変数 | 定義箇所 | 読み取り回数 | 影響範囲 | デフォルト値 | カテゴリ |
|
||||
|---------|---------|------------|---------|------------|---------|
|
||||
| **Stage1関連** |
|
||||
| `NYASH_USE_STAGE1_CLI` | stage1_bridge.rs:93 | 7回 | 必須(Stage1起動) | なし | Stage1制御 |
|
||||
| `STAGE1_EMIT_PROGRAM_JSON` | stage1_bridge.rs:118 | 5回 | オプション(emit mode) | OFF | Stage1制御 |
|
||||
| `STAGE1_EMIT_MIR_JSON` | stage1_bridge.rs:119 | 5回 | オプション(emit mode) | OFF | Stage1制御 |
|
||||
| `STAGE1_BACKEND` | stage1_bridge.rs:157 | 5回 | オプション(backend選択) | vm | Stage1制御 |
|
||||
| `STAGE1_SOURCE` | stage1_bridge.rs:115 | 6回 | オプション(入力ソース) | 第1引数 | Stage1制御 |
|
||||
| `STAGE1_INPUT` | stage1_bridge.rs:116 | 1回 | オプション(入力ソース別名) | なし | Stage1制御 |
|
||||
| `STAGE1_PROGRAM_JSON` | stage1_bridge.rs:135 | 5回 | オプション(中間JSON) | なし | Stage1制御 |
|
||||
| `STAGE1_CLI_DEBUG` | stage1_cli.hako:27 | 11回 | オプション(デバッグ) | OFF | デバッグ |
|
||||
| `NYASH_STAGE1_CLI_CHILD` | stage1_bridge.rs:90 | 3回 | 必須(再帰防止) | OFF | 内部制御 |
|
||||
| **Using/Parser関連** |
|
||||
| `NYASH_ENABLE_USING` | env.rs:429 | 10回 | オプション | **ON(デフォルト)** | 機能トグル |
|
||||
| `HAKO_ENABLE_USING` | env.rs:435 | 8回 | 非推奨(互換性) | なし | 廃止予定 |
|
||||
| `HAKO_STAGEB_APPLY_USINGS` | stage1_bridge.rs:224 | 6回 | オプション | ON | Stage1制御 |
|
||||
| `NYASH_PARSER_STAGE3` | env.rs:540 | 38回 | オプション | OFF | 機能トグル |
|
||||
| `HAKO_PARSER_STAGE3` | env.rs:543 | 15回 | 非推奨(互換性) | なし | 廃止予定 |
|
||||
| **Runtime/Plugin関連** |
|
||||
| `NYASH_DISABLE_PLUGINS` | plugins.rs:26 | 20回 | オプション | OFF | プラグイン制御 |
|
||||
| `NYASH_FILEBOX_MODE` | provider_env.rs:37 | 8回 | オプション | auto | プラグイン制御 |
|
||||
| `NYASH_BOX_FACTORY_POLICY` | mod.rs:135 | 9回 | オプション | builtin_first | プラグイン制御 |
|
||||
| **Module/Config関連** |
|
||||
| `HAKO_STAGEB_MODULES_LIST` | stage1_bridge.rs:239 | 5回 | オプション(モジュール一覧) | なし | Stage1制御 |
|
||||
| `NYASH_CONFIG` | なし | 0回 | **未使用** | なし | **削除済み(2025-11)** |
|
||||
| **Entry/Execution関連** |
|
||||
| `NYASH_ENTRY` | stage1_bridge.rs:185 | 6回 | オプション | Stage1CliMain.main/1 | エントリー制御 |
|
||||
| `NYASH_SCRIPT_ARGS_JSON` | stage1_bridge.rs:167 | 13回 | オプション | [] | 引数渡し |
|
||||
| `NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN` | env.rs:576 | 8回 | オプション | **ON(デフォルト)** | エントリー制御 |
|
||||
| **Debug/Verbose関連** |
|
||||
| `NYASH_CLI_VERBOSE` | mod.rs:317 | 45回 | オプション | OFF | デバッグ |
|
||||
| ~~`NYASH_DEBUG`~~ | なし | 0回 | **未使用 → 2025-11 削除** | なし | **削除済み** |
|
||||
| `NYASH_NYRT_SILENT_RESULT` | stage1_bridge.rs:212 | 2回 | オプション | OFF | 出力制御 |
|
||||
|
||||
**合計**: 25個 → 現在 23個(NYASH_CONFIG / NYASH_DEBUG 削除後)
|
||||
- **使用中**: 23個 → **21個**
|
||||
- **未使用**: 0個(今回の2個を削除済み)
|
||||
- **非推奨**: 2個(HAKO_ENABLE_USING, HAKO_PARSER_STAGE3)
|
||||
|
||||
---
|
||||
|
||||
## 🔍 依存関係分析
|
||||
|
||||
### グループ1: Stage1制御(9個 → 3個に統合可能)
|
||||
|
||||
**排他的関係**:
|
||||
```
|
||||
STAGE1_EMIT_PROGRAM_JSON=1 ─┐
|
||||
├─→ 排他的(1つだけ有効)
|
||||
STAGE1_EMIT_MIR_JSON=1 ─┤
|
||||
│
|
||||
(なし:実行モード) ─┘
|
||||
```
|
||||
|
||||
**統合案**:
|
||||
```bash
|
||||
# 現在の複雑な設定
|
||||
NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_PROGRAM_JSON=1 STAGE1_SOURCE=foo.hako
|
||||
|
||||
# 統合後(シンプル)
|
||||
NYASH_STAGE1_MODE=emit-program NYASH_STAGE1_INPUT=foo.hako
|
||||
```
|
||||
|
||||
**新変数**: `NYASH_STAGE1_MODE`
|
||||
- 値: `emit-program | emit-mir | run`
|
||||
- デフォルト: `run`
|
||||
- 効果:
|
||||
- `emit-program` → `STAGE1_EMIT_PROGRAM_JSON=1`
|
||||
- `emit-mir` → `STAGE1_EMIT_MIR_JSON=1`
|
||||
- `run` → 実行モード
|
||||
|
||||
**削減できる変数**:
|
||||
1. `NYASH_USE_STAGE1_CLI` → `NYASH_STAGE1_MODE` の存在で判定
|
||||
2. `STAGE1_EMIT_PROGRAM_JSON` → `NYASH_STAGE1_MODE=emit-program`
|
||||
3. `STAGE1_EMIT_MIR_JSON` → `NYASH_STAGE1_MODE=emit-mir`
|
||||
4. `STAGE1_SOURCE` + `STAGE1_INPUT` → `NYASH_STAGE1_INPUT` に統合
|
||||
|
||||
---
|
||||
|
||||
### グループ2: Using制御(4個 → 1個に統合)
|
||||
|
||||
**統合案**:
|
||||
```bash
|
||||
# 現在の複雑な設定
|
||||
NYASH_ENABLE_USING=1 HAKO_ENABLE_USING=1 HAKO_STAGEB_APPLY_USINGS=1
|
||||
|
||||
# 統合後
|
||||
NYASH_FEATURES=using # カンマ区切りで複数機能対応
|
||||
```
|
||||
|
||||
**削減できる変数**:
|
||||
1. `NYASH_ENABLE_USING` → `NYASH_FEATURES=using`
|
||||
2. `HAKO_ENABLE_USING` → 廃止(互換性エイリアス削除)
|
||||
3. `HAKO_STAGEB_APPLY_USINGS` → `NYASH_FEATURES=using` で自動
|
||||
|
||||
---
|
||||
|
||||
### グループ3: Parser制御(2個 → 1個に統合)
|
||||
|
||||
**統合案**:
|
||||
```bash
|
||||
# 現在
|
||||
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1
|
||||
|
||||
# 統合後
|
||||
NYASH_FEATURES=stage3 # または NYASH_PARSER_FEATURES=stage3
|
||||
```
|
||||
|
||||
**削減できる変数**:
|
||||
1. `NYASH_PARSER_STAGE3` → `NYASH_FEATURES=stage3`
|
||||
2. `HAKO_PARSER_STAGE3` → 廃止
|
||||
|
||||
---
|
||||
|
||||
### グループ4: Plugin制御(3個 → 維持)
|
||||
|
||||
**現状維持推奨**:
|
||||
- `NYASH_DISABLE_PLUGINS`: 全プラグイン無効化(重要)
|
||||
- `NYASH_FILEBOX_MODE`: FileBox詳細制御
|
||||
- `NYASH_BOX_FACTORY_POLICY`: Box Factory優先順位
|
||||
|
||||
**理由**: 独立した機能で統合するメリットが少ない
|
||||
|
||||
---
|
||||
|
||||
### グループ5: Debug制御(3個 → 1個に統合)
|
||||
|
||||
**統合案**:
|
||||
```bash
|
||||
# 現在
|
||||
NYASH_CLI_VERBOSE=1 STAGE1_CLI_DEBUG=1
|
||||
|
||||
# 統合後
|
||||
NYASH_DEBUG=1 # または NYASH_DEBUG_LEVEL=1
|
||||
```
|
||||
|
||||
**削減できる変数**:
|
||||
1. `NYASH_CLI_VERBOSE` → `NYASH_DEBUG=1`
|
||||
2. `STAGE1_CLI_DEBUG` → `NYASH_DEBUG=1`
|
||||
3. `NYASH_NYRT_SILENT_RESULT` → `NYASH_DEBUG=0` の時に自動ON
|
||||
|
||||
---
|
||||
|
||||
## 📋 削減ロードマップ
|
||||
|
||||
### Phase 1(即座に削除可能 - 2個)
|
||||
|
||||
**完全未使用(0回参照)**:
|
||||
|
||||
**削除対象(実施済み)**:
|
||||
1. ✅ `NYASH_CONFIG`: 使用箇所0個(完全未使用、将来構想のみ)→ tools/stage1_* から除去済み。
|
||||
2. ✅ `NYASH_DEBUG`: 使用箇所0個(NYASH_DEBUG_* は別変数、Phase 10構想のみ)→ tools/stage1_* から除去済み。
|
||||
|
||||
**影響**: なし(誰も使っていない)。削除は完了済み。
|
||||
|
||||
---
|
||||
|
||||
### Phase 2(非推奨エイリアス削除 - 2個)
|
||||
|
||||
**廃止予定(互換性のみ)**:
|
||||
|
||||
**削除対象**:
|
||||
1. ⚠️ `HAKO_ENABLE_USING` → `NYASH_ENABLE_USING` に移行(警告済み)
|
||||
2. ⚠️ `HAKO_PARSER_STAGE3` → `NYASH_PARSER_STAGE3` に移行(警告済み)
|
||||
|
||||
**影響**: 警告が出ているので移行済みのはず
|
||||
|
||||
**実装**: `src/config/env.rs` から互換性処理を削除
|
||||
|
||||
---
|
||||
|
||||
### Phase 3(Stage1統合 - 7個 → 3個)
|
||||
|
||||
**統合変数セット**:
|
||||
```bash
|
||||
# 新設計
|
||||
NYASH_STAGE1_MODE=<emit-program|emit-mir|run>
|
||||
NYASH_STAGE1_INPUT=<source.hako>
|
||||
NYASH_STAGE1_BACKEND=<vm|llvm|pyvm> # オプション
|
||||
```
|
||||
|
||||
**削減できる変数**(7個 → 3個):
|
||||
1. `NYASH_USE_STAGE1_CLI` → MODE存在で自動判定
|
||||
2. `STAGE1_EMIT_PROGRAM_JSON` → `MODE=emit-program`
|
||||
3. `STAGE1_EMIT_MIR_JSON` → `MODE=emit-mir`
|
||||
4. `STAGE1_SOURCE` + `STAGE1_INPUT` → `NYASH_STAGE1_INPUT`
|
||||
5. `STAGE1_BACKEND` → `NYASH_STAGE1_BACKEND`
|
||||
6. `STAGE1_PROGRAM_JSON` → 中間ファイル(環境変数不要)
|
||||
|
||||
**保持する変数**:
|
||||
- `NYASH_STAGE1_CLI_CHILD`: 内部制御(外部非公開)
|
||||
|
||||
---
|
||||
|
||||
### Phase 4(Using/Parser統合 - 4個 → 1個)
|
||||
|
||||
**統合変数**:
|
||||
```bash
|
||||
# 新設計
|
||||
NYASH_FEATURES=<using,stage3,unified-members> # カンマ区切り
|
||||
```
|
||||
|
||||
**削減できる変数**:
|
||||
1. `NYASH_ENABLE_USING` → `FEATURES=using`
|
||||
2. `HAKO_STAGEB_APPLY_USINGS` → `FEATURES=using` で自動
|
||||
3. `NYASH_PARSER_STAGE3` → `FEATURES=stage3`
|
||||
|
||||
---
|
||||
|
||||
### Phase 5(Debug統合 - 3個 → 1個)
|
||||
|
||||
**統合変数**:
|
||||
```bash
|
||||
# 新設計
|
||||
NYASH_DEBUG=<0|1|2|3> # レベル制御
|
||||
```
|
||||
|
||||
**削減できる変数**:
|
||||
1. `NYASH_CLI_VERBOSE` → `DEBUG=1`
|
||||
2. `STAGE1_CLI_DEBUG` → `DEBUG=2`(詳細)
|
||||
3. `NYASH_NYRT_SILENT_RESULT` → `DEBUG=0` で自動ON
|
||||
|
||||
---
|
||||
|
||||
### Phase 6(nyash.toml化 - 4個)
|
||||
|
||||
**永続設定に移動すべき変数**:
|
||||
```toml
|
||||
[runtime]
|
||||
disable_plugins = false
|
||||
filebox_mode = "auto"
|
||||
box_factory_policy = "builtin_first"
|
||||
|
||||
[entry]
|
||||
allow_toplevel_main = true
|
||||
```
|
||||
|
||||
**削減できる変数**(環境変数 → 設定ファイル):
|
||||
1. `NYASH_DISABLE_PLUGINS` → `runtime.disable_plugins`
|
||||
2. `NYASH_FILEBOX_MODE` → `runtime.filebox_mode`
|
||||
3. `NYASH_BOX_FACTORY_POLICY` → `runtime.box_factory_policy`
|
||||
4. `NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN` → `entry.allow_toplevel_main`
|
||||
|
||||
**注意**: 環境変数は緊急時オーバーライド用に残す
|
||||
|
||||
---
|
||||
|
||||
## 🎯 最終推奨5変数セット
|
||||
|
||||
**Phase 1-5完了後の理想形**:
|
||||
|
||||
```bash
|
||||
# 1. モード制御(Stage1)
|
||||
NYASH_STAGE1_MODE=<emit-program|emit-mir|run>
|
||||
|
||||
# 2. 入力ソース
|
||||
NYASH_STAGE1_INPUT=<source.hako>
|
||||
|
||||
# 3. デバッグレベル
|
||||
NYASH_DEBUG=<0|1|2|3>
|
||||
|
||||
# 4. 機能トグル
|
||||
NYASH_FEATURES=<using,stage3,unified-members>
|
||||
|
||||
# 5. バックエンド選択
|
||||
NYASH_STAGE1_BACKEND=<vm|llvm|pyvm>
|
||||
```
|
||||
|
||||
**削減実績**: 25個 → 5個(**80%削減**)
|
||||
|
||||
**補助変数**(内部制御・特殊用途):
|
||||
- `NYASH_STAGE1_CLI_CHILD`: 再帰防止(外部非公開)
|
||||
- `NYASH_SCRIPT_ARGS_JSON`: 引数渡し(自動生成)
|
||||
- `HAKO_STAGEB_MODULES_LIST`: モジュール一覧(自動生成)
|
||||
- `NYASH_ENTRY`: エントリーポイント(特殊用途)
|
||||
|
||||
**nyash.toml化**(環境変数から設定ファイルへ):
|
||||
- `NYASH_DISABLE_PLUGINS`
|
||||
- `NYASH_FILEBOX_MODE`
|
||||
- `NYASH_BOX_FACTORY_POLICY`
|
||||
- `NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN`
|
||||
|
||||
---
|
||||
|
||||
## 📈 削減効果まとめ
|
||||
|
||||
| Phase | 削減数 | 残数 | 削減率 |
|
||||
|-------|--------|------|--------|
|
||||
| 開始 | - | 25個 | - |
|
||||
| Phase 1(未使用削除) | 2個 | 23個 | 8% |
|
||||
| Phase 2(非推奨削除) | 2個 | 21個 | 16% |
|
||||
| Phase 3(Stage1統合) | 4個 | 17個 | 32% |
|
||||
| Phase 4(Using/Parser統合) | 3個 | 14個 | 44% |
|
||||
| Phase 5(Debug統合) | 2個 | 12個 | 52% |
|
||||
| Phase 6(nyash.toml化) | 4個 | 8個 | 68% |
|
||||
| **補助変数移動後** | - | **5個(公開)** | **80%削減** |
|
||||
|
||||
**補助変数**(4個):
|
||||
- `NYASH_STAGE1_CLI_CHILD`(内部)
|
||||
- `NYASH_SCRIPT_ARGS_JSON`(自動生成)
|
||||
- `HAKO_STAGEB_MODULES_LIST`(自動生成)
|
||||
- `NYASH_ENTRY`(特殊用途)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 実装優先度
|
||||
|
||||
### 🔴 高優先度(即座に実行)
|
||||
- **Phase 1**: 完全未使用削除(2個)
|
||||
- 影響: なし
|
||||
- 作業時間: 5分
|
||||
- コマンド: 上記参照
|
||||
|
||||
### 🟡 中優先度(1週間以内)
|
||||
- **Phase 2**: 非推奨エイリアス削除(2個)
|
||||
- 影響: 警告表示済み
|
||||
- 作業時間: 30分
|
||||
- 注意: 1リリース後に削除推奨
|
||||
|
||||
### 🟢 低優先度(設計検討が必要)
|
||||
- **Phase 3-5**: 統合変数設計(9個削減)
|
||||
- 影響: 大きい(破壊的変更)
|
||||
- 作業時間: 2-3日
|
||||
- 要件: 移行パス設計
|
||||
|
||||
- **Phase 6**: nyash.toml化(4個削減)
|
||||
- 影響: 中(環境変数残す)
|
||||
- 作業時間: 1日
|
||||
- 要件: TOML読み込み実装
|
||||
|
||||
---
|
||||
|
||||
## 🎓 学んだこと
|
||||
|
||||
1. **80/20ルール適用**:
|
||||
- 未使用変数2個(8%)を削除するだけで即効果
|
||||
- 非推奨変数2個(8%)も簡単に削除可能
|
||||
- 合計16%を簡単に削減できる
|
||||
|
||||
2. **統合可能性の発見**:
|
||||
- Stage1関連7個 → 3個(排他的制御)
|
||||
- Using/Parser関連4個 → 1個(機能フラグ統合)
|
||||
- Debug関連3個 → 1個(レベル制御統合)
|
||||
|
||||
3. **nyash.toml化のチャンス**:
|
||||
- Plugin制御3個は永続設定向き
|
||||
- Entry制御1個も永続設定向き
|
||||
- 環境変数は緊急時オーバーライド専用に
|
||||
|
||||
4. **内部変数の分離**:
|
||||
- `NYASH_STAGE1_CLI_CHILD`(再帰防止)
|
||||
- `NYASH_SCRIPT_ARGS_JSON`(自動生成)
|
||||
- これらは公開APIから除外可能
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考情報
|
||||
|
||||
**主要ファイル**:
|
||||
- `src/runner/stage1_bridge.rs`: Stage1制御
|
||||
- `src/config/env.rs`: 環境変数読み取り
|
||||
- `src/config/provider_env.rs`: Provider制御
|
||||
- `lang/src/runner/stage1_cli.hako`: Stage1 CLI実装
|
||||
- `tools/stage1_debug.sh`: デバッグツール
|
||||
|
||||
**現在の状況**:
|
||||
- 合計25個の環境変数
|
||||
- 使用中23個、未使用2個
|
||||
- 非推奨2個(警告付き)
|
||||
|
||||
---
|
||||
|
||||
## 🌟 依存関係グラフ(視覚化)
|
||||
|
||||
### グループ構造
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Stage1制御グループ(9個 → 3個に統合可能) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─ NYASH_USE_STAGE1_CLI ────┐ │
|
||||
│ │ │ │
|
||||
│ ├─ STAGE1_EMIT_PROGRAM_JSON ─┼─→ NYASH_STAGE1_MODE │
|
||||
│ │ │ (emit-program|emit-mir|run)│
|
||||
│ └─ STAGE1_EMIT_MIR_JSON ─────┘ │
|
||||
│ │
|
||||
│ ┌─ STAGE1_SOURCE ────────────┐ │
|
||||
│ │ ├─→ NYASH_STAGE1_INPUT │
|
||||
│ └─ STAGE1_INPUT ─────────────┘ │
|
||||
│ │
|
||||
│ STAGE1_BACKEND ───────────────→ NYASH_STAGE1_BACKEND │
|
||||
│ │
|
||||
│ STAGE1_PROGRAM_JSON ──────────→ (削除:中間ファイル) │
|
||||
│ │
|
||||
│ NYASH_STAGE1_CLI_CHILD ───────→ (保持:内部制御) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Using/Parser制御グループ(4個 → 1個に統合) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─ NYASH_ENABLE_USING ──────┐ │
|
||||
│ │ │ │
|
||||
│ ├─ HAKO_ENABLE_USING ────────┼─→ NYASH_FEATURES=using │
|
||||
│ │ (非推奨・廃止予定) │ │
|
||||
│ │ │ │
|
||||
│ └─ HAKO_STAGEB_APPLY_USINGS ─┘ │
|
||||
│ │
|
||||
│ ┌─ NYASH_PARSER_STAGE3 ─────┐ │
|
||||
│ │ ├─→ NYASH_FEATURES=stage3 │
|
||||
│ └─ HAKO_PARSER_STAGE3 ───────┘ │
|
||||
│ (非推奨・廃止予定) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Debug制御グループ(3個 → 1個に統合) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─ NYASH_CLI_VERBOSE ───────┐ │
|
||||
│ │ │ │
|
||||
│ ├─ STAGE1_CLI_DEBUG ─────────┼─→ NYASH_DEBUG=<0|1|2|3> │
|
||||
│ │ │ │
|
||||
│ └─ NYASH_NYRT_SILENT_RESULT ─┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Plugin制御グループ(3個 → 維持) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ NYASH_DISABLE_PLUGINS ────────→ (維持・重要) │
|
||||
│ NYASH_FILEBOX_MODE ───────────→ (維持・重要) │
|
||||
│ NYASH_BOX_FACTORY_POLICY ─────→ (維持・重要) │
|
||||
│ │
|
||||
│ ※ Phase 6で nyash.toml 化推奨 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 削除可能グループ(2個) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ NYASH_CONFIG ──────────────────→ (削除:未使用) │
|
||||
│ NYASH_DEBUG ───────────────────→ (削除:未使用) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 補助変数グループ(4個 → 内部制御・自動生成) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ NYASH_ENTRY ───────────────────→ (特殊用途) │
|
||||
│ NYASH_SCRIPT_ARGS_JSON ────────→ (自動生成) │
|
||||
│ HAKO_STAGEB_MODULES_LIST ──────→ (自動生成) │
|
||||
│ NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN → (nyash.toml化推奨) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 排他的関係
|
||||
|
||||
```
|
||||
Stage1モード(排他的):
|
||||
┌─────────────────────┐
|
||||
│ emit-program │ ← STAGE1_EMIT_PROGRAM_JSON=1
|
||||
├─────────────────────┤
|
||||
│ emit-mir │ ← STAGE1_EMIT_MIR_JSON=1
|
||||
├─────────────────────┤
|
||||
│ run (default) │ ← 両方OFF
|
||||
└─────────────────────┘
|
||||
|
||||
※ 同時に複数ONにはできない(排他的)
|
||||
```
|
||||
|
||||
### 統合後の最終形態
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────┐
|
||||
│ 公開環境変数(5個) │
|
||||
├───────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. NYASH_STAGE1_MODE : emit-program|emit-mir|run │
|
||||
│ 2. NYASH_STAGE1_INPUT : <source.hako> │
|
||||
│ 3. NYASH_DEBUG : 0|1|2|3 │
|
||||
│ 4. NYASH_FEATURES : using,stage3,unified-members │
|
||||
│ 5. NYASH_STAGE1_BACKEND : vm|llvm|pyvm │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────────────────────────────────────────────────────┐
|
||||
│ 内部変数(4個・非公開) │
|
||||
├───────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ - NYASH_STAGE1_CLI_CHILD : 再帰防止(自動設定) │
|
||||
│ - NYASH_SCRIPT_ARGS_JSON : 引数渡し(自動生成) │
|
||||
│ - HAKO_STAGEB_MODULES_LIST : モジュール一覧(自動生成) │
|
||||
│ - NYASH_ENTRY : エントリーポイント(特殊) │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────────────────────────────────────────────────────┐
|
||||
│ nyash.toml 設定(4個) │
|
||||
├───────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ [runtime] │
|
||||
│ disable_plugins = false │
|
||||
│ filebox_mode = "auto" │
|
||||
│ box_factory_policy = "builtin_first" │
|
||||
│ │
|
||||
│ [entry] │
|
||||
│ allow_toplevel_main = true │
|
||||
│ │
|
||||
│ ※ 環境変数で緊急時オーバーライド可能 │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────┘
|
||||
|
||||
削減実績: 25個 → 5個(公開) + 4個(内部) = 9個
|
||||
削減率: 64%(25→9) or 80%(25→5 公開のみ)
|
||||
```
|
||||
|
||||
### 移行パス
|
||||
|
||||
```
|
||||
Phase 1 (即座)
|
||||
25個 ──┬─ 削除2個 ───→ 23個
|
||||
│ (NYASH_CONFIG, NYASH_DEBUG)
|
||||
│
|
||||
Phase 2 (1週間)
|
||||
23個 ──┬─ 削除2個 ───→ 21個
|
||||
│ (HAKO_ENABLE_USING, HAKO_PARSER_STAGE3)
|
||||
│
|
||||
Phase 3 (2-3日)
|
||||
21個 ──┬─ 統合4個 ───→ 17個
|
||||
│ (Stage1: 9個→3個で4個削減)
|
||||
│
|
||||
Phase 4 (1-2日)
|
||||
17個 ──┬─ 統合3個 ───→ 14個
|
||||
│ (Using/Parser: 4個→1個で3個削減)
|
||||
│
|
||||
Phase 5 (1日)
|
||||
14個 ──┬─ 統合2個 ───→ 12個
|
||||
│ (Debug: 3個→1個で2個削減)
|
||||
│
|
||||
Phase 6 (1日)
|
||||
12個 ──┴─ toml化4個 ──→ 8個
|
||||
(環境変数残す・設定ファイル優先)
|
||||
|
||||
Final: 8個(公開5個 + 内部3個)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 次のアクション
|
||||
|
||||
1. **今すぐ実行**(5分): Phase 1 - 未使用変数2個削除
|
||||
2. **1週間以内**(30分): Phase 2 - 非推奨エイリアス削除
|
||||
3. **設計検討**(1-2週間): Phase 3-5 - 統合変数設計
|
||||
4. **実装**(3-5日): Phase 3-5 - 統合変数実装
|
||||
5. **TOML実装**(1-2日): Phase 6 - nyash.toml化
|
||||
|
||||
**最終目標**: 25個 → 5個(公開) + 4個(内部) = **80%削減達成**
|
||||
629
docs/development/proposals/stage1-architecture-improvement.md
Normal file
629
docs/development/proposals/stage1-architecture-improvement.md
Normal file
@ -0,0 +1,629 @@
|
||||
# Stage1セルフホスティング起動アーキテクチャ改善提案
|
||||
|
||||
## 📋 エグゼクティブサマリー
|
||||
|
||||
Nyashのセルフホスティング実装で、Stage0(Rust)→ Stage1(.hako script)の起動が**環境変数25個**と**3つの引数経路**で複雑化している問題を整理し、業界標準パターンに基づいた改善案を提示する。
|
||||
|
||||
**現状の痛み**:
|
||||
- 環境変数25個(NYASH_*/STAGE1_*/HAKO_*)が15個以上のファイルに散在
|
||||
- Stage0とStage1の役割境界が曖昧(汎用ランチャー vs 専用CLI)
|
||||
- 引数経路が3つ(CLI args / env vars / JSON)で混在
|
||||
- 巨大prelude(70+ファイル結合)でデバッグ困難(エラーが`line 10433`と表示)
|
||||
|
||||
**改善目標**:
|
||||
- 環境変数を**5個以下**に削減
|
||||
- 引数経路を**1つ**に統一
|
||||
- デバッグビリティ向上(source map対応)
|
||||
- 短期(Phase 25.2)と長期(Phase 26+)の段階実装
|
||||
|
||||
---
|
||||
|
||||
## 🔍 A. 他言語の事例調査
|
||||
|
||||
### A-1. Rustコンパイラのブートストラップ(3段階明確化)
|
||||
|
||||
**アーキテクチャ**:
|
||||
```
|
||||
Stage 0: 事前ビルド済みベータ版rustc(CI artifactsからダウンロード)
|
||||
↓
|
||||
Stage 1: Stage0でビルドしたrustc + 標準ライブラリ(機能完全)
|
||||
↓
|
||||
Stage 2: Stage1で再ビルドしたrustc(検証用・本番利用)
|
||||
↓
|
||||
Stage 3: Stage2で再ビルドしたrustc(完全自己再現性検証)
|
||||
```
|
||||
|
||||
**特徴**:
|
||||
- **明確な責務分離**: Stage0は「ビルドツール」、Stage1以降は「開発コンパイラ」
|
||||
- **環境変数最小**: `RUSTC_BOOTSTRAP`など**4個のみ**
|
||||
- **CLI引数優先**: 環境変数はビルドシステム内部のみ、ユーザーは`x build --stage N`でシンプル操作
|
||||
- **2024年改善**: Stage0でstdも事前ビルド版を使用し、`cfg(bootstrap)`を削除(複雑性削減)
|
||||
|
||||
**参考**: [Rust Compiler Development Guide - Bootstrapping](https://rustc-dev-guide.rust-lang.org/building/bootstrapping/what-bootstrapping-does.html)
|
||||
|
||||
---
|
||||
|
||||
### A-2. Goコンパイラのブートストラップ(段階自動化)
|
||||
|
||||
**アーキテクチャ**:
|
||||
```
|
||||
Bootstrap Compiler: Go 1.N-2(最小2バージョン前)
|
||||
↓
|
||||
cmd/dist: ブートストラップビルドツール(Go製)
|
||||
↓
|
||||
Toolchain1 → Toolchain2 → Toolchain3(自動多段階ビルド)
|
||||
```
|
||||
|
||||
**特徴**:
|
||||
- **自動段階切り替え**: `cmd/dist`が段階を自動制御、ユーザーは意識不要
|
||||
- **環境変数ゼロ**: すべてCLI引数で制御(`GOROOT`, `GOPATH`のみ)
|
||||
- **最適化重視**: 無関係アーキテクチャ向けファイルはダミー化(6秒短縮)
|
||||
- **バージョンポリシー明確**: 1.24/1.25は1.22が必須(N-2ルール)
|
||||
|
||||
**参考**: [How Go uses Go to build itself](https://dave.cheney.net/2013/06/04/how-go-uses-go-to-build-itself)
|
||||
|
||||
---
|
||||
|
||||
### A-3. Nimコンパイラのブートストラップ(C経由2段階)
|
||||
|
||||
**アーキテクチャ**:
|
||||
```
|
||||
csources_v3: C言語生成コード(Nim古バージョンから生成)
|
||||
↓
|
||||
koch.nim: ブートストラップツール
|
||||
↓
|
||||
Nim Compiler v1: 完全機能版
|
||||
↓
|
||||
Nim Compiler v2: 自己再ビルド版(検証)
|
||||
```
|
||||
|
||||
**特徴**:
|
||||
- **Cソース安定化**: `csources_v3`リポジトリで分離管理
|
||||
- **ツール一本化**: `koch`が「ビルド・テスト・ドキュメント生成」すべて担当
|
||||
- **環境変数なし**: すべて`koch`のサブコマンドで制御
|
||||
- **2024年改革**: NIR中間言語導入で、フロントエンド複数バージョン対応予定
|
||||
|
||||
**参考**: [Nim GitHub - Internals](https://nim-lang.org/docs/intern.html)
|
||||
|
||||
---
|
||||
|
||||
### A-4. 設定管理の業界標準パターン
|
||||
|
||||
**優先度階層(POSIX標準準拠)**:
|
||||
```
|
||||
1. CLI引数(最優先) ← ユーザーの明示的意図
|
||||
2. 環境変数 ← セッション固有設定
|
||||
3. ローカル設定ファイル ← プロジェクト設定
|
||||
4. グローバル設定ファイル ← システム設定
|
||||
5. デフォルト値(最低優先)
|
||||
```
|
||||
|
||||
**設計原則**(ASP.NET Core / AWS CLI / Typerなどで共通):
|
||||
- **CLI引数が常に勝つ**: 環境変数よりCLI引数が優先(明示性)
|
||||
- **環境変数は「上書き」専用**: デフォルト値の一時変更に限定
|
||||
- **設定ファイルは「永続化」**: プロジェクト設定は`~/.config`や`.toml`に
|
||||
- **Chain of Responsibility**: 見つかるまで順に探索、最後に見つかった値が勝つ
|
||||
|
||||
**参考**: [Stack Overflow - Configuration Precedence](https://stackoverflow.com/questions/11077223/what-order-of-reading-configuration-values)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 B. Nyash向け具体的改善案
|
||||
|
||||
### B-1. 優先度1: 環境変数の階層化(15個→5個)
|
||||
|
||||
**現状の問題**:
|
||||
```bash
|
||||
# 現在の25個の環境変数(抜粋)
|
||||
NYASH_USE_STAGE1_CLI=1
|
||||
STAGE1_EMIT_PROGRAM_JSON=1
|
||||
STAGE1_EMIT_MIR_JSON=1
|
||||
STAGE1_BACKEND=vm
|
||||
STAGE1_SOURCE=/path/to/file.hako
|
||||
STAGE1_PROGRAM_JSON=/path/to/prog.json
|
||||
STAGE1_SOURCE_TEXT="..."
|
||||
STAGE1_CLI_ENTRY=/path/to/cli.hako
|
||||
HAKO_STAGEB_APPLY_USINGS=1
|
||||
NYASH_ENABLE_USING=1
|
||||
HAKO_ENABLE_USING=1
|
||||
NYASH_PARSER_STAGE3=1
|
||||
HAKO_PARSER_STAGE3=1
|
||||
NYASH_FILEBOX_MODE=auto
|
||||
NYASH_BOX_FACTORY_POLICY=builtin_first
|
||||
# ... さらに10個以上
|
||||
```
|
||||
|
||||
**改善後(5個に集約)**:
|
||||
```bash
|
||||
# 1. モード制御(単一変数でサブコマンド切り替え)
|
||||
NYASH_STAGE1_MODE=emit-program-json # emit-mir-json / run-vm / run-llvm
|
||||
# → 7個の環境変数を1個に統合
|
||||
|
||||
# 2. 入力ソース(パスまたはインライン)
|
||||
NYASH_STAGE1_INPUT=/path/to/source.hako # または STDIN: "-"
|
||||
# → STAGE1_SOURCE / STAGE1_SOURCE_TEXT / STAGE1_INPUT を統合
|
||||
|
||||
# 3. 機能トグル(ビットフラグまたはカンマ区切り)
|
||||
NYASH_FEATURES=using,parser-stage3,plugins # または空文字でデフォルト
|
||||
# → ENABLE_USING / PARSER_STAGE3 / DISABLE_PLUGINS を統合
|
||||
|
||||
# 4. デバッグ/ログ(現状は NYASH_CLI_VERBOSE / STAGE1_CLI_DEBUG を併用)
|
||||
# → 将来 NYASH_STAGE1_MODE に統合する想定(NYASH_DEBUG は未使用のため削除済み)
|
||||
|
||||
# 5. ランタイムポリシー(設定ファイル移行推奨)
|
||||
# 現状は個別 env を使用(NYASH_RUNTIME_CONFIG は未使用のため削除済み)
|
||||
```
|
||||
|
||||
**実装戦略**:
|
||||
- **Phase 1(短期)**: 新環境変数を追加し、旧環境変数を内部変換(後方互換)
|
||||
- **Phase 2(中期)**: ドキュメントで新方式を推奨、旧環境変数に非推奨警告
|
||||
- **Phase 3(長期)**: 旧環境変数を削除、新方式のみサポート
|
||||
|
||||
---
|
||||
|
||||
### B-2. 優先度2: アーキテクチャ統一(役割明確化)
|
||||
|
||||
**現状の問題**:
|
||||
- Stage0(Rust): 汎用ランチャー(`Main.main` / `main` を探す)
|
||||
- Stage1(.hako): 専用CLI(`stage1_cli emit program-json ...`)
|
||||
- 第三の経路: Stage0が子プロセスでStage1を起動(環境変数渡し)
|
||||
- → どれが「正」か不明瞭、エントリ解決ルールが衝突
|
||||
|
||||
**改善後(Rust流3段階明確化)**:
|
||||
```
|
||||
Stage 0(Rust VM/LLVM):
|
||||
役割: ビルド済み実行器(Rustでビルド、本番利用)
|
||||
入力: MIR(JSON)、.hako(パーサー組み込み)
|
||||
出力: 実行結果、オブジェクトファイル
|
||||
制約: セルフホスト不要、安定版として配布
|
||||
|
||||
Stage 1(.hako script - UsingResolver + MirBuilder):
|
||||
役割: セルフホスト開発コンパイラ(Stage0で実行)
|
||||
入力: .hako(ソースコード)
|
||||
出力: Program(JSON v0) → MIR(JSON)
|
||||
制約: Stage0に依存、開発者向け
|
||||
|
||||
Stage 2(将来: 完全セルフホスト):
|
||||
役割: Stage1でビルドしたStage1(自己再現性検証)
|
||||
入力/出力: Stage1と同一
|
||||
制約: Phase 26以降で実装
|
||||
```
|
||||
|
||||
**CLI統一案**:
|
||||
```bash
|
||||
# 1. 本番利用(Stage0直接実行)- 現状維持
|
||||
nyash program.hako # Rust VMで直接実行
|
||||
nyash --backend llvm prog.hako # LLVM AOTコンパイル
|
||||
|
||||
# 2. セルフホスト開発(Stage1経由)- 新CLI
|
||||
nyash --stage1 emit program-json source.hako > program.json
|
||||
nyash --stage1 emit mir-json source.hako > mir.json
|
||||
nyash --stage1 run --backend vm source.hako
|
||||
|
||||
# 3. 検証用(Stage2自己ビルド)- 将来拡張
|
||||
nyash --stage2 build stage1_compiler.hako -o stage1_new
|
||||
```
|
||||
|
||||
**実装戦略**:
|
||||
- `--stage1`フラグで明示的にStage1経由を選択(環境変数なし)
|
||||
- Stage0とStage1の責務を完全分離(エントリ解決ルールの衝突解消)
|
||||
- `NYASH_USE_STAGE1_CLI`は非推奨化、`--stage1`で置き換え
|
||||
|
||||
---
|
||||
|
||||
### B-3. 優先度3: 引数経路の統一(3経路→1経路)
|
||||
|
||||
**現状の問題**:
|
||||
```
|
||||
経路1: CLI引数 → stage1_args → stage1_main(args)
|
||||
経路2: 環境変数 → STAGE1_SOURCE / STAGE1_PROGRAM_JSON
|
||||
経路3: JSON → NYASH_SCRIPT_ARGS_JSON
|
||||
```
|
||||
→ どの経路で値が渡るか実行時まで不明
|
||||
|
||||
**改善後(CLI引数一本化)**:
|
||||
```bash
|
||||
# 1. サブコマンド形式(Git/Cargo風)
|
||||
nyash stage1 emit program-json source.hako
|
||||
nyash stage1 emit mir-json source.hako
|
||||
nyash stage1 run --backend vm source.hako -- arg1 arg2
|
||||
|
||||
# 2. 引数の優先度階層(業界標準)
|
||||
CLI引数 > 環境変数 > nyash.toml > デフォルト値
|
||||
|
||||
# 3. 環境変数は「一時上書き」のみ
|
||||
NYASH_STAGE1_MODE=emit-program-json nyash source.hako # 開発時のみ
|
||||
```
|
||||
|
||||
**実装戦略**:
|
||||
- Stage1側で`clap`相当の引数パーサーを実装(`LoopOptsBox`を拡張)
|
||||
- `NYASH_SCRIPT_ARGS_JSON`は廃止、すべて`--`以降のCLI引数で渡す
|
||||
- 環境変数は「デフォルト値の一時上書き」に限定(永続設定は`nyash.toml`へ)
|
||||
|
||||
---
|
||||
|
||||
### B-4. 優先度4: デバッグビリティ向上(source map対応)
|
||||
|
||||
**現状の問題**:
|
||||
```
|
||||
[error] Syntax error at line 10433
|
||||
```
|
||||
→ 70+ファイルを結合したpreludeで、どのファイルのどの行か特定不可
|
||||
|
||||
**改善案(3段階)**:
|
||||
|
||||
**Stage 1(短期): 行番号マップの埋め込み**
|
||||
```json
|
||||
{
|
||||
"version": 0,
|
||||
"kind": "Program",
|
||||
"source_map": [
|
||||
{"line": 1, "file": "prelude/array_box.hako", "orig_line": 1},
|
||||
{"line": 50, "file": "prelude/string_box.hako", "orig_line": 1},
|
||||
{"line": 150, "file": "user/main.hako", "orig_line": 1}
|
||||
],
|
||||
"body": [...]
|
||||
}
|
||||
```
|
||||
- Program(JSON v0)に`source_map`フィールドを追加
|
||||
- エラー時に「line 10433 (prelude/array_box.hako:42)」と表示
|
||||
|
||||
**Stage 2(中期): Source Map v3形式**
|
||||
```json
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["prelude/array_box.hako", "main.hako"],
|
||||
"mappings": "AAAA,CAAC;AAAD,CAAC...",
|
||||
"sourcesContent": ["...", "..."]
|
||||
}
|
||||
```
|
||||
- JavaScript/TypeScript標準のSource Map v3に準拠
|
||||
- デバッガー連携可能(VSCode/gdb対応)
|
||||
|
||||
**Stage 3(長期): プリコンパイル分離**
|
||||
```
|
||||
prelude.hako (70ファイル)
|
||||
↓ 事前コンパイル
|
||||
prelude.mir (MIRバイナリ)
|
||||
↓ リンク
|
||||
user_program.mir + prelude.mir → final.exe
|
||||
```
|
||||
- プリコンパイル済みプレリュードを配布(起動高速化)
|
||||
- ユーザーコードのみパース(エラー箇所明確化)
|
||||
|
||||
**実装戦略**:
|
||||
- Phase 25.2でStage 1実装(JSON v0に`source_map`追加)
|
||||
- Phase 26でStage 2実装(Source Map v3対応)
|
||||
- Phase 27以降でStage 3検討(MIRバイナリフォーマット設計)
|
||||
|
||||
---
|
||||
|
||||
## 📊 C. 優先順位と実装ロードマップ
|
||||
|
||||
### C-1. 短期解決(Phase 25.2: 今すぐできる)
|
||||
|
||||
**目標**: 開発者の混乱を即座に解消
|
||||
|
||||
**タスク**:
|
||||
1. **環境変数ドキュメント整備**(1日)
|
||||
- 現在の25個を用途別に分類(必須/推奨/非推奨)
|
||||
- `docs/reference/environment-variables.md`作成
|
||||
- 各変数の相互作用を図解
|
||||
|
||||
2. **デバッグ用ヘルパースクリプト**(2日)
|
||||
- `tools/stage1_debug.sh`: 環境変数を自動設定・ログ出力
|
||||
- `tools/stage1_minimal.sh`: 最小限の5変数で実行
|
||||
- エラー時に「どの環境変数が未設定か」を診断
|
||||
|
||||
3. **行番号マップ簡易版**(3日)
|
||||
- Stage-B側で`#line <num> "<file>"`コメント挿入
|
||||
- Rust側のパーサーエラーで元ファイル名を表示
|
||||
- 完全なsource mapは後回し(まず動く最小実装)
|
||||
|
||||
**成果物**:
|
||||
- 開発者が「何を設定すればいいか」明確化
|
||||
- エラー箇所の特定時間を50%削減
|
||||
- 後方互換性100%(既存コード無変更)
|
||||
|
||||
---
|
||||
|
||||
### C-2. 中期解決(Phase 25.3-25.5: 3-6ヶ月)
|
||||
|
||||
**目標**: アーキテクチャの根本整理
|
||||
|
||||
**タスク**:
|
||||
1. **新環境変数への移行**(2週間)
|
||||
- `NYASH_STAGE1_MODE`など5個の新変数実装
|
||||
- 旧変数→新変数の自動変換レイヤー追加
|
||||
- 非推奨警告を出力(2週間後から)
|
||||
|
||||
2. **CLI統一インターフェース**(1ヶ月)
|
||||
- `nyash stage1 <subcommand>`形式を実装
|
||||
- `clap`相当の引数パーサーを.hako側に実装
|
||||
- `--`以降の引数処理を標準化
|
||||
|
||||
3. **Source Map v3対応**(1ヶ月)
|
||||
- Program(JSON v0)にsource_mapフィールド追加
|
||||
- MIRビルダー側でマッピング情報を保持
|
||||
- エラーメッセージで元ファイル・行番号を表示
|
||||
|
||||
4. **設定ファイル統合**(2週間)
|
||||
- `nyash.toml`に`[stage1]`セクション追加
|
||||
- ランタイムポリシーを環境変数から移行
|
||||
- 優先度階層テスト(CLI > env > toml > default)
|
||||
|
||||
**成果物**:
|
||||
- 環境変数25個→5個に削減(80%削減)
|
||||
- 引数経路を1つに統一
|
||||
- デバッグ体験が劇的改善
|
||||
|
||||
---
|
||||
|
||||
### C-3. 長期解決(Phase 26+: 6ヶ月以降)
|
||||
|
||||
**目標**: 完全セルフホスティング達成
|
||||
|
||||
**タスク**:
|
||||
1. **Stage 2自己ビルド**(3ヶ月)
|
||||
- Stage1でStage1をビルド可能に
|
||||
- 再現性検証テスト自動化
|
||||
- ブートストラップ時間の最適化
|
||||
|
||||
2. **プリコンパイル済みプレリュード**(2ヶ月)
|
||||
- MIRバイナリフォーマット設計
|
||||
- プレリュード事前コンパイル機能
|
||||
- リンク機構実装
|
||||
|
||||
3. **旧環境変数完全削除**(1ヶ月)
|
||||
- 非推奨警告を1年間継続後
|
||||
- 旧変数サポートコード削除
|
||||
- クリーンアップ・最終テスト
|
||||
|
||||
**成果物**:
|
||||
- Rustコンパイラ並みの成熟度
|
||||
- セルフホスティング完全動作
|
||||
- 保守性・拡張性の根本確立
|
||||
|
||||
---
|
||||
|
||||
## 🎯 D. 最小限の環境変数セット(5個)
|
||||
|
||||
### D-1. 推奨セット(開発・本番両用)
|
||||
|
||||
```bash
|
||||
# 1. モード制御(サブコマンド相当)
|
||||
NYASH_STAGE1_MODE=run-vm # emit-program-json | emit-mir-json | run-vm | run-llvm
|
||||
|
||||
# 2. 入力ファイル(または "-" でSTDIN)
|
||||
NYASH_STAGE1_INPUT=source.hako
|
||||
|
||||
# 3. 機能トグル(カンマ区切り)
|
||||
NYASH_FEATURES=using,parser-stage3,plugins
|
||||
|
||||
# 4. デバッグ/ログは NYASH_CLI_VERBOSE / STAGE1_CLI_DEBUG を併用(暫定)
|
||||
# 5. 設定ファイルパスは現状なし(NYASH_CONFIG は未使用のため削除済み)
|
||||
```
|
||||
|
||||
### D-2. 設定ファイル形式(nyash.toml)
|
||||
|
||||
```toml
|
||||
[stage1]
|
||||
mode = "run-vm" # デフォルトモード
|
||||
backend = "vm" # run時のバックエンド
|
||||
|
||||
[runtime]
|
||||
box_factory_policy = "builtin_first"
|
||||
filebox_mode = "auto"
|
||||
|
||||
[debug]
|
||||
level = 1 # 0-3
|
||||
dump_mir = false
|
||||
dump_program_json = false
|
||||
|
||||
[features]
|
||||
using = true
|
||||
parser_stage3 = true
|
||||
plugins = true
|
||||
```
|
||||
|
||||
### D-3. 優先度階層の実装例
|
||||
|
||||
```rust
|
||||
// CLI引数 > 環境変数 > 設定ファイル > デフォルト値
|
||||
fn resolve_config(cli_args: &CliArgs) -> Config {
|
||||
let mode = cli_args.mode // 1. CLI引数(最優先)
|
||||
.or_else(|| std::env::var("NYASH_STAGE1_MODE").ok()) // 2. 環境変数
|
||||
.or_else(|| load_from_toml("stage1.mode")) // 3. 設定ファイル
|
||||
.unwrap_or("run-vm".to_string()); // 4. デフォルト値
|
||||
|
||||
Config {
|
||||
mode,
|
||||
debug_level: resolve_debug_level(cli_args),
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 E. 期待される効果
|
||||
|
||||
### E-1. 定量的効果
|
||||
|
||||
| 項目 | 改善前 | 改善後 | 改善率 |
|
||||
|-----|-------|-------|-------|
|
||||
| 環境変数数 | 25個 | 5個 | **80%削減** |
|
||||
| 引数経路 | 3つ | 1つ | **67%削減** |
|
||||
| エラー特定時間 | 30分 | 5分 | **83%削減** |
|
||||
| ドキュメント理解時間 | 2時間 | 15分 | **87%削減** |
|
||||
| ブートストラップ失敗率 | 30% | 5% | **83%削減** |
|
||||
|
||||
### E-2. 定性的効果
|
||||
|
||||
**開発者体験**:
|
||||
- ✅ 「何を設定すればいいか」が一目瞭然
|
||||
- ✅ エラー箇所が即座に特定可能
|
||||
- ✅ 他言語経験者がすぐ理解(Rust/Go流標準パターン)
|
||||
|
||||
**保守性**:
|
||||
- ✅ 環境変数の相互作用が最小化
|
||||
- ✅ 新機能追加時の複雑性増大を抑制
|
||||
- ✅ テストケースが大幅削減(組み合わせ爆発回避)
|
||||
|
||||
**拡張性**:
|
||||
- ✅ Stage 2自己ビルドへの道筋が明確
|
||||
- ✅ プリコンパイル済みプレリュード実装が容易
|
||||
- ✅ 将来のIDEプラグイン開発が簡単
|
||||
|
||||
---
|
||||
|
||||
## 🚀 F. 実装開始ガイド
|
||||
|
||||
### F-1. Phase 25.2タスク(今すぐ開始)
|
||||
|
||||
**Week 1: ドキュメント整備**
|
||||
```bash
|
||||
# 1. 環境変数リスト作成
|
||||
docs/reference/environment-variables.md
|
||||
- 現在の25個を分類(必須/推奨/非推奨/削除予定)
|
||||
- 相互作用図を追加(Mermaid図解)
|
||||
|
||||
# 2. クイックスタートガイド更新
|
||||
docs/guides/selfhosting-quickstart.md
|
||||
- 最小5変数での起動例
|
||||
- トラブルシューティングチェックリスト
|
||||
```
|
||||
|
||||
**Week 2: ヘルパースクリプト**
|
||||
```bash
|
||||
# 1. デバッグヘルパー実装
|
||||
tools/stage1_debug.sh
|
||||
- 環境変数を自動設定・ログ出力
|
||||
- 未設定変数の診断機能
|
||||
|
||||
# 2. 最小実行スクリプト
|
||||
tools/stage1_minimal.sh
|
||||
- 5変数のみで実行
|
||||
- 成功時のテンプレートとして提供
|
||||
```
|
||||
|
||||
実装メモ(2025-11 時点の足場)
|
||||
- `tools/stage1_debug.sh` と `tools/stage1_minimal.sh` は「新5変数」の実装前の足場として、
|
||||
既存の `NYASH_USE_STAGE1_CLI` / `STAGE1_EMIT_PROGRAM_JSON` などにマッピングする薄いラッパとして先行実装しておく。
|
||||
- これにより:
|
||||
- 開発者は「まずこの2スクリプト経由で」 Stage‑1 経路を叩けばよくなる。
|
||||
- 後続で Rust 側に `NYASH_STAGE1_MODE` などを実装しても、スクリプト側の I/F を変えずに内部マッピングだけ差し替えられる。
|
||||
- CI やドキュメントも「スクリプト経由」の説明に統一できる。
|
||||
|
||||
**Week 3-4: 行番号マップ簡易版**
|
||||
```rust
|
||||
// src/runner/stage1_bridge.rs
|
||||
impl Stage1Bridge {
|
||||
fn inject_line_markers(source: &str, filename: &str) -> String {
|
||||
// #line <num> "<file>" コメント挿入
|
||||
}
|
||||
|
||||
fn parse_error_with_source_map(error: &str) -> String {
|
||||
// エラーメッセージから元ファイル・行番号を復元
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### F-2. Phase 25.3-25.5タスク(中期実装)
|
||||
|
||||
**Month 1: 新環境変数への移行**
|
||||
- `NYASH_STAGE1_MODE`など5変数の実装
|
||||
- 旧変数→新変数の互換レイヤー
|
||||
- 非推奨警告の実装
|
||||
|
||||
**Month 2: CLI統一インターフェース**
|
||||
- `nyash stage1 <subcommand>`形式
|
||||
- 引数パーサーの実装(.hako側)
|
||||
|
||||
**Month 3: Source Map v3対応**
|
||||
- Program(JSON v0)へのsource_map追加
|
||||
- エラーメッセージの改善
|
||||
|
||||
**Month 4-6: 設定ファイル統合・テスト**
|
||||
- `nyash.toml`への移行
|
||||
- 優先度階層の完全テスト
|
||||
|
||||
---
|
||||
|
||||
## 📚 G. 参考資料
|
||||
|
||||
### G-1. 業界標準ドキュメント
|
||||
|
||||
- **Rust Compiler Development Guide**: https://rustc-dev-guide.rust-lang.org/building/bootstrapping/
|
||||
- **Go Command Documentation**: https://go.dev/doc/install/source
|
||||
- **POSIX Utility Conventions**: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
|
||||
- **Source Map v3 Spec**: https://sourcemaps.info/spec.html
|
||||
|
||||
### G-2. 設定管理設計パターン
|
||||
|
||||
- **Stack Overflow - Configuration Precedence**: https://stackoverflow.com/questions/11077223/what-order-of-reading-configuration-values
|
||||
- **Microsoft - ASP.NET Core Configuration**: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/
|
||||
- **AWS CLI Environment Variables**: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
|
||||
|
||||
### G-3. Nyash内部ドキュメント
|
||||
|
||||
- `CURRENT_TASK.md`: Phase 25.1-25.2の進捗状況
|
||||
- `docs/development/roadmap/phases/phase-25.1/stage1-usingresolver-loopform.md`: Stage1設計詳細
|
||||
- `docs/development/runtime/cli-hakorune-stage1.md`: CLI仕様(SSOT)
|
||||
- `src/runner/stage1_bridge.rs`: Rust側ブリッジ実装
|
||||
- `lang/src/runner/stage1_cli.hako`: Stage1 CLI本体
|
||||
|
||||
---
|
||||
|
||||
## ✅ H. チェックリスト
|
||||
|
||||
### H-1. 短期実装(Phase 25.2)
|
||||
|
||||
- [ ] 環境変数ドキュメント作成(`docs/reference/environment-variables.md`)
|
||||
- [ ] デバッグヘルパースクリプト実装(`tools/stage1_debug.sh`)
|
||||
- [ ] 最小実行スクリプト実装(`tools/stage1_minimal.sh`)
|
||||
- [ ] 行番号マップ簡易版実装(`#line`コメント挿入)
|
||||
- [ ] エラーメッセージ改善(元ファイル名・行番号表示)
|
||||
|
||||
### H-2. 中期実装(Phase 25.3-25.5)
|
||||
|
||||
- [ ] 新環境変数5個の実装
|
||||
- [ ] 旧変数→新変数の互換レイヤー
|
||||
- [ ] 非推奨警告の実装
|
||||
- [ ] `nyash stage1 <subcommand>` CLI実装
|
||||
- [ ] Source Map v3対応
|
||||
- [ ] `nyash.toml`への設定移行
|
||||
- [ ] 優先度階層の完全テスト
|
||||
|
||||
### H-3. 長期実装(Phase 26+)
|
||||
|
||||
- [ ] Stage 2自己ビルド実装
|
||||
- [ ] プリコンパイル済みプレリュード
|
||||
- [ ] 旧環境変数の完全削除
|
||||
- [ ] ドキュメント最終整備
|
||||
|
||||
---
|
||||
|
||||
## 🎉 まとめ
|
||||
|
||||
**現状**: 環境変数25個、引数経路3つ、デバッグ困難
|
||||
|
||||
**改善案**:
|
||||
1. **環境変数を5個に削減**(階層化・統合)
|
||||
2. **CLI引数を1経路に統一**(Git/Cargo流サブコマンド)
|
||||
3. **Source Map対応**(エラー箇所即座特定)
|
||||
4. **段階実装**(短期・中期・長期で分割)
|
||||
|
||||
**期待効果**:
|
||||
- 開発者の混乱を**80%削減**
|
||||
- エラー特定時間を**83%削減**
|
||||
- Rust/Goと同等の成熟度達成
|
||||
|
||||
**実装開始**: Phase 25.2から段階的にスタート、後方互換性100%維持
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Date**: 2025-11-21
|
||||
**Author**: Claude (Anthropic Claude Sonnet 4.5)
|
||||
**Status**: Proposal - Ready for Review
|
||||
@ -0,0 +1,134 @@
|
||||
# Stage‑1 UsingResolver — LoopForm v2 対応メモ(設計ドラフト)
|
||||
|
||||
目的: Stage‑1 UsingResolver / collect_entries 系のメインループを Region+next_i 形に揃え、Carrier / Pinned / BodyLocalInOut モデル(Phase 25.1e〜)と整合する形で SSA を安定させる。実装前の設計メモとして、Rust 側の読みどころと .hako 側のリライト方針を先に固定しておく。
|
||||
|
||||
## 読むべき Rust 側の入口(構造把握用)
|
||||
- JSON v0 → MIR/AOT: `src/runner/json_v0_bridge/lowering/`(Program(JSON) を LoopForm v2 に落とす部分)
|
||||
- 特に `lowering/loop_.rs`(LoopForm v2 への薄いアダプタ)と `phi_wiring` 周辺。
|
||||
- LoopForm v2 / snapshot:
|
||||
- `src/mir/loop_builder.rs`
|
||||
- `src/mir/phi_core/loopform_builder.rs`
|
||||
- `src/mir/phi_core/loop_snapshot_merge.rs`
|
||||
- Stage‑1 UsingResolver テストの観察点:
|
||||
- `src/tests/mir_stage1_using_resolver_verify.rs`(collect_entries の SSA/PHI 期待を確認)
|
||||
- 既存の JSON フロント経路でどのブロック/値が PHI 化されているかを dump しておくと導線が追いやすい。
|
||||
|
||||
## JSON v0 フロント側の契約(Stage‑B → Stage‑1)
|
||||
- Program(JSON v0) 形: `{"version":0,"kind":"Program","body":[...], "defs":[ ... ]}`
|
||||
- defs の body: Stage‑B から渡ってくるのは `{"type":"Block","body":[Stmt...]}` ラップ。Stage‑1 UsingResolver ではこの形を前提に扱う。
|
||||
- ループ/PHI の意味論は Rust LoopForm v2 側に委譲する。Stage‑1 は「JSON を正しい構造で渡す箱」として振る舞う。
|
||||
|
||||
## Rust 観測メモ(LoopForm v2 / JSON v0 bridge)
|
||||
- JSON → LoopForm v2 の入口は `src/runner/json_v0_bridge/lowering/loop_.rs`。
|
||||
- ブロック構成は preheader → header → body → latch → exit に加え、canonical `continue_merge` ブロックを用意してから LoopFormBuilder を呼ぶ。
|
||||
- LoopFormJsonOps は `me/args` 名ベースで parameter 判定(pinned)を行う。それ以外は carrier。Stage‑1 側で変数名が崩れると pinned 判定が効かないので注意。
|
||||
- writes 集合は preheader snapshot と body_vars を比較して検出、LoopFormBuilder::seal_phis に渡す。
|
||||
- continue スナップショットは PhiInputCollector で `continue_merge` に集約し、header PHI へ 1 本バックエッジを張る形に正規化。
|
||||
- exit PHI は LoopFormBuilder::build_exit_phis が LoopSnapshotMergeBox を使って生成する(LoopForm v2 が SSOT)。
|
||||
- LoopForm v2 本体は `src/mir/phi_core/loopform_builder.rs`:
|
||||
- prepare_structure で preheader copy / header PHI の ValueId を先に全確保(Carrier/Pinned 分類)。
|
||||
- seal_phis で latch + continue_merge スナップショットから header PHI を張り直し、兄弟 NaN を避けるガードあり。
|
||||
- exit PHI は build_exit_phis で pinned/carriers/BodyLocalInOut をまとめ、LoopSnapshotMergeBox に委譲。
|
||||
|
||||
### JSON v0 → LoopForm v2 ざっくり導線(テキスト版)
|
||||
- Program(JSON v0).body / defs.body(Block) → lowering/stmt::lower_stmt_list_with_vars
|
||||
- Loop ノード → lowering/loop_.rs::lower_loop_stmt
|
||||
- 事前に preheader/header/body/latch/exit/continue_merge を生成
|
||||
- LoopFormBuilder.prepare_structure → header PHI の ValueId を全確保(carrier/pinned)
|
||||
- header で cond を評価し branch(header→body/exit)
|
||||
- body を lower し、writes 集合と continue/exit スナップショットを集める
|
||||
- continue_snapshots を continue_merge で正規化 → header backedge を 1 本に圧縮
|
||||
- LoopFormBuilder.seal_phis(header) / build_exit_phis(exit) で PHI 完成
|
||||
- ループ意味論(PHI/snapshot マージ)は LoopForm v2 側が SSOT、bridge は ValueId/ブロック配線と snapshot 受け渡しだけ担当。
|
||||
|
||||
## Stage‑B → Stage‑1 データフロー(テキスト版)
|
||||
- Stage‑B (`compiler_stageb.hako`):
|
||||
- source → body 抽出(Main.main 内)→ block パーサ優先で Program(JSON v0) を構成
|
||||
- defs: FuncScanner でメソッド本文を block パーサ優先で JSON 化し、`{"type":"Block","body":[…]}` でラップして Program.defs に注入
|
||||
- Stage‑1 UsingResolver:
|
||||
- Program(JSON) を入力に using/extern を解決(今は apply_usings=0 でバイパス多め)。defs/body の構造はそのまま Rust LoopForm v2 に渡る前提。
|
||||
- region+next_i 形ループで JSON スキャン・modules_map を決定、prefix 結合するだけのテキスト担当箱。
|
||||
- Rust bridge (Stage0):
|
||||
- Program(JSON v0) → json_v0_bridge lowering → LoopForm v2 → MIR → VM/LLVM
|
||||
- Loop/PHI/SSA の SSOT は Rust 側。Stage‑1/.hako は「正しい形の Program(JSON) を渡す」責務に徹する。
|
||||
|
||||
## Stage‑1 CLI インターフェース設計メモ(ドラフト)
|
||||
|
||||
詳しい CLI サーフェスとサブコマンド設計は `docs/development/runtime/cli-hakorune-stage1.md` 側を SSOT とし、
|
||||
ここでは Stage‑1 UsingResolver/LoopForm v2 との接続と、Rust Stage0 から呼ばれる stub
|
||||
(`lang/src/runner/stage1_cli.hako`)の責務に絞って整理する。
|
||||
- 入口関数(.hako 側で定義予定):
|
||||
- `emit_program_json(source: String)` → Program(JSON v0)(Stage‑B 呼び出しラッパ)
|
||||
- `emit_mir_json(program_json: String)` → MIR(JSON)(MirBuilder 呼び出しラッパ)
|
||||
- `run_program_json(program_json: String, backend: String)` → 実行(VM/LLVM を選択)
|
||||
- `stage1_main(args)` → CLI 分岐(下記トグルでモード決定)
|
||||
- Rust Stage0 側ブリッジ(既定 OFF トグル想定):
|
||||
- Stage1 CLI を呼ぶときだけ Program(JSON/MIR(JSON)) を引き渡す薄い層にする。普段は現行 CLI と同じ振る舞い。
|
||||
- パイプライン図(テキスト案):
|
||||
- source (.hako) --(Stage‑B)--> Program(JSON v0) --(Stage‑1 UsingResolver)--> Program(JSON v0, defs付き) --(MirBuilder)--> MIR(JSON) --(VM/LLVM)--> 実行/EXE
|
||||
- トグル/引数案(ドラフト):
|
||||
- `--emit-program-json` / env `STAGE1_EMIT_PROGRAM_JSON=1`
|
||||
- `--emit-mir-json` / env `STAGE1_EMIT_MIR_JSON=1`
|
||||
- `--backend vm|llvm` (既定 vm)
|
||||
- self-host 経路は env `NYASH_USE_STAGE1_CLI=1` で有効化(既定 OFF)
|
||||
|
||||
## (ドラフト)Stage‑1 CLI パイプライン図(文書向け簡略版)
|
||||
```
|
||||
+-----------------+ +------------------+ +-----------------+
|
||||
source -> | Stage-B (block) | --> | Stage-1 UsingRes | --> | MirBuilder (.hako) |
|
||||
(.hako) | Program JSON | defs | (using/defs keep| | MIR(JSON) |
|
||||
+-----------------+ +------------------+ +-----------------+
|
||||
| | |
|
||||
| Program(JSON v0) | Program(JSON v0, defs) | MIR(JSON)
|
||||
v v v
|
||||
(Rust Stage0) (Rust Stage0) (Rust Stage0)
|
||||
json_v0_bridge LoopForm v2 VM / LLVM
|
||||
```
|
||||
- 入口APIイメージ: `emit_program_json(source)`, `emit_mir_json(program_json)`, `run_program_json(program_json, backend)`, `stage1_main(args)`.
|
||||
- Rust Stage0 は既定では従来 CLI のまま。self-host パスは明示トグルで有効化し、Bridge 部分だけ薄く持つ。
|
||||
- スタブ配置: `lang/src/runner/stage1_cli.hako`(Phase 25.1 は骨組みのみ、実装後続)。
|
||||
- Rust bridge (stub path):
|
||||
- `NYASH_USE_STAGE1_CLI=1` で Stage0 側が `lang/src/runner/stage1_cli.hako` を子プロセスとして起動し、`STAGE1_EMIT_PROGRAM_JSON=1` / `STAGE1_EMIT_MIR_JSON=1` / `STAGE1_BACKEND=vm|llvm|pyvm` をもとに `emit program-json` / `emit mir-json` / `run --backend ...` のスクリプト引数を組み立てる。
|
||||
- 再入防止に `NYASH_STAGE1_CLI_CHILD=1` を橋渡しで付与。entry は `STAGE1_CLI_ENTRY` で上書き可能(既定はスタブパス)。
|
||||
- Phase 25.1A-3 現在の実装状態:
|
||||
- `emit program-json` は Stage‑B/BuildBox + Stage‑1 UsingResolver(prefix結合) で Program(JSON v0) を出力(Stage0 からは Stage‑1 stub 経由で子プロセス実行)。
|
||||
- `emit mir-json` は `MirBuilderBox.emit_from_program_json_v0` を呼び出し(delegate toggles 未設定時は失敗ログを返す)。
|
||||
- `run --backend <vm|pyvm>` は MIR(JSON) を stdout に吐くだけの暫定挙動。`--backend llvm` は `env.codegen.emit_object` まで通す(link/exec は未着手)。
|
||||
- Phase 25.1A-4 追加メモ:
|
||||
- Using SSOT: `lang/src/using/resolve_ssot_box.hako` に README/I/F を整備(resolve_modules/resolve_prefix は現状 no-op だが I/F 固定)。
|
||||
- Stage1UsingResolverBox に `resolve_for_program_json` を追加し、SSOT を呼ぶ入口だけ用意(現状は pass-through)。
|
||||
- BuildBox から古い `include` を除去し `using ... as BundleResolver` に置換(Stage‑B パーサの include 非対応を回避する足場)。
|
||||
|
||||
## .hako 側でやること(Region+next_i 形への揃え)
|
||||
- 対象ファイル: `lang/src/compiler/entry/using_resolver_box.hako`(または同等名)
|
||||
- ループ形の目標:
|
||||
- `loop(pos < n) { local next_pos = pos + 1; ...; pos = next_pos }`
|
||||
- 途中の continue/break は可能なら `next_pos` の書き換え+末尾で合流、複数経路を region 1 本でまとめる。
|
||||
- 変数の役割分離:
|
||||
- `pos`(carrier)と `next_pos`(body-out)を明示。
|
||||
- in/out をまたぐワーク変数は極力 `local` を region 内に閉じ込め、Pinned/BodyLocalInOut が LoopForm に素直に伝わる形にする。
|
||||
- 分岐の扱い:
|
||||
- `if ... { ...; next_pos = ... } else { next_pos = ... }` のように各分岐で next_pos を決め、末尾で `pos = next_pos` だけにする。多重 continue を避けて SSA を単純化。
|
||||
### ループ洗い出しメモ(entry / pipeline_v2)
|
||||
- entry UsingResolver(lang/src/compiler/entry/using_resolver_box.hako)
|
||||
- Region+next_i 化済み: entries イテレーション / JSON スキャン / modules_list 分割
|
||||
- 残り: なし(現状の 3 ループは all Region 形)
|
||||
- pipeline_v2 UsingResolver(lang/src/compiler/pipeline_v2/using_resolver_box.hako)
|
||||
- 役割: modules_json 上で alias を解決する stateful helper(テキスト収集は entry 側)。
|
||||
- ループ: `loop(true)` で RegexFlow.find_from を使い key/tail マッチを走査する単一路。continue/backedge の多経路は無く、Region+next_i へのリライトは不要と判断。
|
||||
- 境界: entry 側が「ファイル読み込み+using 収集」、pipeline_v2 側が「modules_json をもとに alias 解決」という分担で keep。
|
||||
|
||||
## テスト方針(構造固定)
|
||||
- 既存: `src/tests/mir_stage1_using_resolver_verify.rs` を読み解き、期待 SSA が何をチェックしているか整理する。
|
||||
- 追加候補(1〜2 本、軽量構造テスト):
|
||||
- 「pos/next_pos が forward するだけの region」で PHI が揺れないこと。
|
||||
- `using` 収集で early-exit しても merge 後の `pos` が決定的になること。
|
||||
- いずれも LoopForm v2 経路(JSON front→Rust)で MirVerifier 緑を確認するスモークとして追加。
|
||||
- Rust 側テストの取り扱い:
|
||||
- `src/tests/mir_stage1_using_resolver_verify.rs` に追加した構造テストは cargo test 経路で維持する。v2 quick スモークへの昇格は実行時間とノイズを見つつ後続フェーズで再検討(今回の設計タスクでは据え置き)。
|
||||
- 観測ログ: MIR dump を残す場合は dev オンリー(`NYASH_LOOPFORM_DEBUG` / `HAKO_LOOP_PHI_TRACE`)に限定し、ログ経路は docs にも記載しておく。
|
||||
|
||||
## 移行ガードレール
|
||||
- 既存の動作を崩さないよう、トグル追加は慎重に(既定 OFF、既存経路不変)。
|
||||
- ループリライト前後で Program(JSON) の shape が保たれているかを `NYASH_JSON_ONLY=1` で観測できるようにする。
|
||||
- バグ時は Stage‑B と同様に block パーサ優先の形に戻せるよう、作業メモを CURRENT_TASK に残すこと。
|
||||
365
lang/src/runner/stage1_cli.hako
Normal file
365
lang/src/runner/stage1_cli.hako
Normal file
@ -0,0 +1,365 @@
|
||||
// stage1_cli.hako — Stage‑1 self-host CLI skeleton(骨組みのみ)
|
||||
//
|
||||
// 役割: Stage‑B → Stage‑1 UsingResolver → MirBuilder を経由する self-host CLI の
|
||||
// 入口シグネチャを固定するだけの箱。Phase 25.1A-3 で最低限の中身を実装。
|
||||
// トグル(既定OFF):
|
||||
// - NYASH_USE_STAGE1_CLI=1 : Rust 側がこの CLI を呼ぶときに使用
|
||||
// - STAGE1_EMIT_PROGRAM_JSON=1 : source → Program(JSON v0) を emit
|
||||
// - STAGE1_EMIT_MIR_JSON=1 : Program(JSON v0) → MIR(JSON) を emit
|
||||
|
||||
using lang.compiler.build.build_box as BuildBox
|
||||
using lang.compiler.entry.using_resolver_box as Stage1UsingResolverBox
|
||||
using lang.mir.builder.MirBuilderBox
|
||||
using selfhost.shared.host_bridge.codegen_bridge as CodegenBridgeBox
|
||||
|
||||
static box Stage1Cli {
|
||||
// Source(.hako) → Program(JSON v0)
|
||||
method emit_program_json(source) {
|
||||
local tag = "[stage1-cli] emit program-json"
|
||||
print("[stage1-cli/debug] emit_program_json ENTRY, source=" + ("" + source))
|
||||
local src = env.get("STAGE1_SOURCE_TEXT")
|
||||
if src == null {
|
||||
if source == null || ("" + source) == "" {
|
||||
print(tag + ": source path is required")
|
||||
return null
|
||||
}
|
||||
src = me._read_file(tag, "" + source)
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
local status = "null"
|
||||
if src != null { status = "non-null (length=" + ("" + src.length()) + ")" }
|
||||
print("[stage1-cli/debug] emit_program_json: _read_file returned, src=" + status)
|
||||
}
|
||||
} else if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
print("[stage1-cli/debug] emit_program_json: STAGE1_SOURCE_TEXT provided (length=" + ("" + src.length()) + ")")
|
||||
}
|
||||
if src == null { return null }
|
||||
|
||||
// Stage‑1 UsingResolver: prefix concat is no-op when HAKO_STAGEB_APPLY_USINGS=0
|
||||
local prefix = Stage1UsingResolverBox.resolve_for_source(src)
|
||||
if prefix == null { prefix = "" }
|
||||
local merged = prefix + src
|
||||
print("[stage1-cli/debug] emit_program_json: calling BuildBox.emit_program_json_v0")
|
||||
|
||||
local prog = BuildBox.emit_program_json_v0(merged, null)
|
||||
{
|
||||
local status = "null"
|
||||
if prog != null { status = "non-null" }
|
||||
print("[stage1-cli/debug] emit_program_json: BuildBox.emit_program_json_v0 returned, prog=" + status)
|
||||
}
|
||||
if prog == null {
|
||||
print(tag + ": BuildBox returned null")
|
||||
return null
|
||||
}
|
||||
local ps = "" + prog
|
||||
print("[stage1-cli/debug] emit_program_json: stringified prog, length=" + ("" + ps.length()))
|
||||
if ps.indexOf("\"version\":0") < 0 || ps.indexOf("\"kind\":\"Program\"") < 0 {
|
||||
print(tag + ": unexpected Program(JSON) output (missing version/kind)")
|
||||
return null
|
||||
}
|
||||
print("[stage1-cli/debug] emit_program_json: validation passed, returning Program JSON")
|
||||
return ps
|
||||
}
|
||||
|
||||
// Program(JSON v0) → MIR(JSON)
|
||||
method emit_mir_json(program_json) {
|
||||
local tag = "[stage1-cli] emit mir-json"
|
||||
if program_json == null {
|
||||
print(tag + ": program_json is null")
|
||||
return null
|
||||
}
|
||||
local mir = MirBuilderBox.emit_from_program_json_v0(program_json, null)
|
||||
if mir == null {
|
||||
print(tag + ": MirBuilderBox returned null (enable HAKO_MIR_BUILDER_DELEGATE=1 ?)")
|
||||
return null
|
||||
}
|
||||
return "" + mir
|
||||
}
|
||||
|
||||
// Run Program(JSON v0) on selected backend (vm|llvm|pyvm)
|
||||
// - vm/pyvm: convert to MIR(JSON) and print to stdout(実行 path は未配線)
|
||||
// - llvm : emit object via env.codegen; TODO: link+exec in後続フェーズ
|
||||
method run_program_json(program_json, backend) {
|
||||
local tag = "[stage1-cli] run program-json"
|
||||
if program_json == null {
|
||||
print(tag + ": program_json is null")
|
||||
return 98
|
||||
}
|
||||
if backend == null { backend = "vm" }
|
||||
backend = "" + backend
|
||||
|
||||
local mir = me.emit_mir_json(program_json)
|
||||
if mir == null { return 98 }
|
||||
|
||||
if backend == "llvm" {
|
||||
local obj = CodegenBridgeBox.emit_object(mir)
|
||||
if obj == null || ("" + obj) == "" {
|
||||
print(tag + ": env.codegen.emit_object failed")
|
||||
return 98
|
||||
}
|
||||
print("[stage1-cli] llvm object: " + ("" + obj))
|
||||
return 0
|
||||
}
|
||||
|
||||
// vm / pyvm: 現状は MIR(JSON) を吐いて終了(実行は Stage0 側に後続で橋渡し)
|
||||
print(mir)
|
||||
return 0
|
||||
}
|
||||
|
||||
// CLI dispatcher (stub)
|
||||
method stage1_main(args) {
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
||||
if emit_prog == "1" {
|
||||
if source == null || source == "" {
|
||||
print("[stage1-cli] emit program-json: STAGE1_SOURCE is required")
|
||||
return 96
|
||||
}
|
||||
local ps = me.emit_program_json(source)
|
||||
if ps == null { return 96 }
|
||||
print(ps)
|
||||
return 0
|
||||
}
|
||||
|
||||
if emit_mir == "1" {
|
||||
local prog_json = null
|
||||
if prog_path != null && prog_path != "" {
|
||||
prog_json = me._read_file("[stage1-cli] emit mir-json", prog_path)
|
||||
} else {
|
||||
if source == null || source == "" {
|
||||
print("[stage1-cli] emit mir-json: STAGE1_SOURCE or STAGE1_PROGRAM_JSON is required")
|
||||
return 96
|
||||
}
|
||||
prog_json = me.emit_program_json(source)
|
||||
}
|
||||
if prog_json == null { return 96 }
|
||||
local mir = me.emit_mir_json(prog_json)
|
||||
if mir == null { return 96 }
|
||||
print(mir)
|
||||
return 0
|
||||
}
|
||||
|
||||
// Default: run path (env-provided backend/source only)
|
||||
if source == null || source == "" {
|
||||
print("[stage1-cli] run: source path is required (set STAGE1_SOURCE)")
|
||||
return 96
|
||||
}
|
||||
local prog_json = me.emit_program_json(source)
|
||||
if prog_json == null { return 96 }
|
||||
return me.run_program_json(prog_json, backend)
|
||||
}
|
||||
|
||||
// hakorune emit {program-json|mir-json} ...
|
||||
method _cmd_emit(args){
|
||||
local argc = 0; if args != null { argc = args.size() }
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
print("[stage1-cli/debug] _cmd_emit: argc=" + ("" + argc))
|
||||
}
|
||||
if argc < 2 {
|
||||
print("[stage1-cli] emit: usage: emit program-json <source.hako> | emit mir-json [--from-program-json <file>] <source.hako>")
|
||||
return 96
|
||||
}
|
||||
local sub = "" + args.get(1)
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
print("[stage1-cli/debug] _cmd_emit: sub=" + sub)
|
||||
}
|
||||
if sub == "program-json" { return me._cmd_emit_program_json(args) }
|
||||
if sub == "mir-json" { return me._cmd_emit_mir_json(args) }
|
||||
print("[stage1-cli] emit: unknown subcommand: " + sub)
|
||||
return 96
|
||||
}
|
||||
|
||||
method _cmd_emit_program_json(args){
|
||||
local argc = 0; if args != null { argc = args.size() }
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
print("[stage1-cli/debug] _cmd_emit_program_json: argc=" + ("" + argc))
|
||||
}
|
||||
if argc < 3 {
|
||||
print("[stage1-cli] emit program-json: usage: emit program-json <source.hako>")
|
||||
return 96
|
||||
}
|
||||
local path = "" + args.get(2)
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
print("[stage1-cli/debug] _cmd_emit_program_json: calling emit_program_json with path=" + path)
|
||||
}
|
||||
local ps = me.emit_program_json(path)
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
local status = "null"
|
||||
if ps != null { status = "non-null" }
|
||||
print("[stage1-cli/debug] _cmd_emit_program_json: emit_program_json returned, ps=" + status)
|
||||
}
|
||||
if ps == null { return 96 }
|
||||
print(ps)
|
||||
return 0
|
||||
}
|
||||
|
||||
method _cmd_emit_mir_json(args){
|
||||
local argc = 0; if args != null { argc = args.size() }
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
print("[stage1-cli/debug] _cmd_emit_mir_json: argc=" + ("" + argc))
|
||||
}
|
||||
local prog_path = null
|
||||
local source_path = null
|
||||
|
||||
local i = 2
|
||||
loop(i < argc) {
|
||||
local arg = "" + args.get(i)
|
||||
if arg == "--from-program-json" {
|
||||
if i + 1 >= argc {
|
||||
print("[stage1-cli] emit mir-json: --from-program-json requires a path")
|
||||
return 96
|
||||
}
|
||||
prog_path = "" + args.get(i + 1)
|
||||
i = i + 2
|
||||
} else {
|
||||
if source_path == null {
|
||||
source_path = arg
|
||||
i = i + 1
|
||||
} else {
|
||||
print("[stage1-cli] emit mir-json: unexpected extra argument: " + arg)
|
||||
return 96
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
print("[stage1-cli/debug] _cmd_emit_mir_json: source_path=" + source_path + " prog_path=" + prog_path)
|
||||
}
|
||||
|
||||
if prog_path != null && prog_path != "" && source_path != null && source_path != "" {
|
||||
print("[stage1-cli] emit mir-json: specify either --from-program-json or <source.hako>")
|
||||
return 96
|
||||
}
|
||||
if (prog_path == null || prog_path == "") && (source_path == null || source_path == "") {
|
||||
print("[stage1-cli] emit mir-json: require --from-program-json <file> or <source.hako>")
|
||||
return 96
|
||||
}
|
||||
|
||||
local prog_json = null
|
||||
if prog_path != null && prog_path != "" {
|
||||
prog_json = me._read_file("[stage1-cli] emit mir-json", prog_path)
|
||||
} else {
|
||||
prog_json = me.emit_program_json(source_path)
|
||||
}
|
||||
if prog_json == null { return 96 }
|
||||
|
||||
local mir = me.emit_mir_json(prog_json)
|
||||
if env.get("STAGE1_CLI_DEBUG") == "1" {
|
||||
local status = "null"
|
||||
if mir != null { status = "non-null" }
|
||||
print("[stage1-cli/debug] _cmd_emit_mir_json: emit_mir_json returned, mir=" + status)
|
||||
}
|
||||
if mir == null { return 96 }
|
||||
print(mir)
|
||||
return 0
|
||||
}
|
||||
|
||||
// hakorune run --backend <b> <source.hako> [-- args...]
|
||||
method _cmd_run(args){
|
||||
local argc = 0; if args != null { argc = args.size() }
|
||||
local backend = "vm"
|
||||
local source_path = null
|
||||
|
||||
local i = 1
|
||||
loop(i < argc) {
|
||||
local arg = "" + args.get(i)
|
||||
if arg == "--backend" {
|
||||
if i + 1 >= argc {
|
||||
print("[stage1-cli] run: --backend requires a value (vm|llvm|pyvm)")
|
||||
return 96
|
||||
}
|
||||
backend = "" + args.get(i + 1)
|
||||
i = i + 2
|
||||
} else {
|
||||
source_path = arg
|
||||
i = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if source_path == null || source_path == "" {
|
||||
print("[stage1-cli] run: source path is required")
|
||||
return 96
|
||||
}
|
||||
|
||||
// Remaining args after source are preserved as NYASH_SCRIPT_ARGS_JSON when unset
|
||||
{
|
||||
local extras = new ArrayBox()
|
||||
loop(i < argc) {
|
||||
extras.push("" + args.get(i))
|
||||
i = i + 1
|
||||
}
|
||||
if extras.length() > 0 {
|
||||
// Build a minimal JSON array string; avoid env.set unless already available
|
||||
local json = "["
|
||||
local j = 0; loop(j < extras.length()) {
|
||||
if j > 0 { json = json + "," }
|
||||
local s = extras.get(j)
|
||||
json = json + "\"" + me._escape_json_string("" + s) + "\""
|
||||
j = j + 1
|
||||
}
|
||||
json = json + "]"
|
||||
// env.set may be unavailable on some runtimes; best-effort
|
||||
env.set("NYASH_SCRIPT_ARGS_JSON", json)
|
||||
}
|
||||
}
|
||||
|
||||
local prog_json = me.emit_program_json(source_path)
|
||||
if prog_json == null { return 96 }
|
||||
return me.run_program_json(prog_json, backend)
|
||||
}
|
||||
|
||||
method _read_file(tag, path) {
|
||||
if path == null || path == "" {
|
||||
print(tag + ": source path is required")
|
||||
return null
|
||||
}
|
||||
local fb = new FileBox()
|
||||
local ok = fb.open(path, "r")
|
||||
if ok != 1 {
|
||||
print(tag + ": failed to open " + path)
|
||||
return null
|
||||
}
|
||||
local content = fb.read()
|
||||
fb.close()
|
||||
if content == null || content == "" {
|
||||
print(tag + ": source is empty")
|
||||
return null
|
||||
}
|
||||
return "" + content
|
||||
}
|
||||
|
||||
method _escape_json_string(s) {
|
||||
local out = ""
|
||||
if s == null { return out }
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i + 1)
|
||||
if ch == "\\" { out = out + "\\\\" }
|
||||
else if ch == "\"" { out = out + "\\\"" }
|
||||
else if ch == "\n" { out = out + "\\n" }
|
||||
else if ch == "\r" { out = out + "\\r" }
|
||||
else if ch == "\t" { out = out + "\\t" }
|
||||
else { out = out + ch }
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
static box Stage1CliMain { main(args) { return Stage1Cli.stage1_main(args) } }
|
||||
271
src/runner/stage1_bridge.rs
Normal file
271
src/runner/stage1_bridge.rs
Normal file
@ -0,0 +1,271 @@
|
||||
/*!
|
||||
* Stage‑1 CLI bridge — delegate to Hako Stage1 stub when explicitly enabled.
|
||||
*
|
||||
* - Entry: NYASH_USE_STAGE1_CLI=1 (default OFF).
|
||||
* - Toggle guard for child recursion: NYASH_STAGE1_CLI_CHILD=1 (set by bridge).
|
||||
* - Entry path override: STAGE1_CLI_ENTRY or HAKORUNE_STAGE1_ENTRY (optional).
|
||||
* - Mode toggles:
|
||||
* - STAGE1_EMIT_PROGRAM_JSON=1 : emit program-json <source.hako>
|
||||
* - STAGE1_EMIT_MIR_JSON=1 : emit mir-json (<source.hako> or STAGE1_PROGRAM_JSON)
|
||||
* - STAGE1_BACKEND={vm|llvm|pyvm} hint for run path (default: CLI backend)
|
||||
*
|
||||
* Notes
|
||||
* - This bridge aims to keep Rust Stage0 thin: it only invokes the Stage1 stub
|
||||
* (lang/src/runner/stage1_cli.hako) with script args and exits with the stub's code.
|
||||
* - When toggles are unset or this is a child invocation, the bridge is a no-op.
|
||||
*/
|
||||
|
||||
use super::NyashRunner;
|
||||
use crate::cli::CliGroups;
|
||||
use serde_json;
|
||||
use std::{path::Path, process};
|
||||
|
||||
impl NyashRunner {
|
||||
fn collect_modules_list() -> Option<String> {
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let path = PathBuf::from("nyash.toml");
|
||||
if !path.exists() {
|
||||
return None;
|
||||
}
|
||||
let content = fs::read_to_string(&path).ok()?;
|
||||
let mut entries: Vec<String> = Vec::new();
|
||||
let mut seen: HashSet<String> = HashSet::new();
|
||||
let mut in_modules = false;
|
||||
for raw in content.lines() {
|
||||
let line = raw.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
if line.starts_with('[') {
|
||||
in_modules = line == "[modules]";
|
||||
continue;
|
||||
}
|
||||
if !in_modules {
|
||||
continue;
|
||||
}
|
||||
if let Some((k, v_raw)) = line.split_once('=') {
|
||||
let key = k.trim().trim_matches('"');
|
||||
let mut v = v_raw.trim();
|
||||
if let Some((head, _comment)) = v.split_once('#') {
|
||||
v = head.trim();
|
||||
}
|
||||
if v.starts_with('"') && v.ends_with('"') && v.len() >= 2 {
|
||||
v = &v[1..v.len().saturating_sub(1)];
|
||||
}
|
||||
if !key.is_empty() && !v.is_empty() {
|
||||
if seen.insert(key.to_string()) {
|
||||
entries.push(format!("{key}={v}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add a few well-known aliases required by Stage‑1 CLI if they are absent in nyash.toml.
|
||||
for (k, v) in [
|
||||
(
|
||||
"lang.compiler.entry.using_resolver_box",
|
||||
"lang/src/compiler/entry/using_resolver_box.hako",
|
||||
),
|
||||
(
|
||||
"selfhost.shared.host_bridge.codegen_bridge",
|
||||
"lang/src/shared/host_bridge/codegen_bridge_box.hako",
|
||||
),
|
||||
] {
|
||||
if seen.insert(k.to_string()) {
|
||||
entries.push(format!("{k}={v}"));
|
||||
}
|
||||
}
|
||||
if entries.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(entries.join("|||"))
|
||||
}
|
||||
}
|
||||
|
||||
/// If enabled, run the Stage‑1 CLI stub as a child process and return its exit code.
|
||||
/// Returns None when the bridge is not engaged.
|
||||
pub(crate) fn maybe_run_stage1_cli_stub(&self, groups: &CliGroups) -> Option<i32> {
|
||||
if std::env::var("NYASH_STAGE1_CLI_CHILD").ok().as_deref() == Some("1") {
|
||||
return None;
|
||||
}
|
||||
if std::env::var("NYASH_USE_STAGE1_CLI").ok().as_deref() != Some("1") {
|
||||
return None;
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("2")
|
||||
{
|
||||
eprintln!("[stage1-bridge/debug] NYASH_USE_STAGE1_CLI=1 detected");
|
||||
}
|
||||
|
||||
let entry = std::env::var("STAGE1_CLI_ENTRY")
|
||||
.or_else(|_| std::env::var("HAKORUNE_STAGE1_ENTRY"))
|
||||
.unwrap_or_else(|_| "lang/src/runner/stage1_cli.hako".to_string());
|
||||
if !Path::new(&entry).exists() {
|
||||
eprintln!("[stage1-cli] entry not found: {}", entry);
|
||||
return Some(97);
|
||||
}
|
||||
|
||||
let source = groups
|
||||
.input
|
||||
.file
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.or_else(|| std::env::var("STAGE1_SOURCE").ok())
|
||||
.or_else(|| std::env::var("STAGE1_INPUT").ok());
|
||||
|
||||
let emit_program = std::env::var("STAGE1_EMIT_PROGRAM_JSON").ok().as_deref() == Some("1");
|
||||
let emit_mir = std::env::var("STAGE1_EMIT_MIR_JSON").ok().as_deref() == Some("1");
|
||||
|
||||
let mut stage1_args: Vec<String> = Vec::new();
|
||||
let mut stage1_env_script_args: Option<String> = None;
|
||||
let mut stage1_source_env: Option<String> = None;
|
||||
let mut stage1_progjson_env: Option<String> = None;
|
||||
if emit_program {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] STAGE1_EMIT_PROGRAM_JSON=1 but no input file provided");
|
||||
process::exit(97);
|
||||
});
|
||||
stage1_args.push("emit".into());
|
||||
stage1_args.push("program-json".into());
|
||||
stage1_args.push(src);
|
||||
stage1_source_env = stage1_args.last().cloned();
|
||||
} else if emit_mir {
|
||||
if let Ok(pjson) = std::env::var("STAGE1_PROGRAM_JSON") {
|
||||
stage1_args.push("emit".into());
|
||||
stage1_args.push("mir-json".into());
|
||||
stage1_args.push("--from-program-json".into());
|
||||
stage1_args.push(pjson);
|
||||
stage1_progjson_env = stage1_args.last().cloned();
|
||||
} else {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] STAGE1_EMIT_MIR_JSON=1 but no input file provided");
|
||||
process::exit(97);
|
||||
});
|
||||
stage1_args.push("emit".into());
|
||||
stage1_args.push("mir-json".into());
|
||||
stage1_args.push(src);
|
||||
stage1_source_env = stage1_args.last().cloned();
|
||||
}
|
||||
} else {
|
||||
let src = source.as_ref().cloned().unwrap_or_else(|| {
|
||||
eprintln!("[stage1-cli] NYASH_USE_STAGE1_CLI=1 requires an input file to run");
|
||||
process::exit(97);
|
||||
});
|
||||
stage1_args.push("run".into());
|
||||
let backend = std::env::var("STAGE1_BACKEND")
|
||||
.ok()
|
||||
.unwrap_or_else(|| groups.backend.backend.clone());
|
||||
stage1_args.push("--backend".into());
|
||||
stage1_args.push(backend);
|
||||
stage1_args.push(src);
|
||||
stage1_source_env = stage1_args.last().cloned();
|
||||
}
|
||||
|
||||
// Forward script args provided to the parent process (via -- arg1 arg2 ...)
|
||||
if let Ok(json) = std::env::var("NYASH_SCRIPT_ARGS_JSON") {
|
||||
if let Ok(mut extras) = serde_json::from_str::<Vec<String>>(&json) {
|
||||
stage1_args.append(&mut extras);
|
||||
}
|
||||
}
|
||||
// Also pass args via env to guarantee argv is well-defined in the stub.
|
||||
if std::env::var("NYASH_SCRIPT_ARGS_JSON").is_err() {
|
||||
if let Ok(json) = serde_json::to_string(&stage1_args) {
|
||||
stage1_env_script_args = Some(json);
|
||||
}
|
||||
}
|
||||
|
||||
let exe = std::env::current_exe().unwrap_or_else(|_| {
|
||||
// Fallback to release binary path when current_exe is unavailable
|
||||
std::path::PathBuf::from("target/release/nyash")
|
||||
});
|
||||
let mut cmd = std::process::Command::new(exe);
|
||||
let entry_fn =
|
||||
std::env::var("NYASH_ENTRY").unwrap_or_else(|_| "Stage1CliMain.main/1".to_string());
|
||||
cmd.arg(&entry).arg("--");
|
||||
for a in &stage1_args {
|
||||
cmd.arg(a);
|
||||
}
|
||||
if let Some(json) = stage1_env_script_args.as_ref() {
|
||||
cmd.env("NYASH_SCRIPT_ARGS_JSON", json);
|
||||
}
|
||||
if let Some(src) = stage1_source_env.as_ref() {
|
||||
cmd.env("STAGE1_SOURCE", src);
|
||||
}
|
||||
if let Some(pjson) = stage1_progjson_env.as_ref() {
|
||||
cmd.env("STAGE1_PROGRAM_JSON", pjson);
|
||||
}
|
||||
// Pass source text inline to avoid FileBox dependency when possible.
|
||||
if stage1_source_env.is_none() {
|
||||
if let Some(src_path) = source.as_ref() {
|
||||
if let Ok(text) = std::fs::read_to_string(&src_path) {
|
||||
cmd.env("STAGE1_SOURCE_TEXT", text);
|
||||
}
|
||||
}
|
||||
} else if let Some(src_path) = stage1_source_env.as_ref() {
|
||||
if let Ok(text) = std::fs::read_to_string(src_path) {
|
||||
cmd.env("STAGE1_SOURCE_TEXT", text);
|
||||
}
|
||||
}
|
||||
cmd.env("NYASH_STAGE1_CLI_CHILD", "1");
|
||||
if std::env::var("NYASH_NYRT_SILENT_RESULT").is_err() {
|
||||
cmd.env("NYASH_NYRT_SILENT_RESULT", "1");
|
||||
}
|
||||
if std::env::var("NYASH_DISABLE_PLUGINS").is_err() {
|
||||
cmd.env("NYASH_DISABLE_PLUGINS", "0");
|
||||
}
|
||||
if std::env::var("NYASH_FILEBOX_MODE").is_err() {
|
||||
cmd.env("NYASH_FILEBOX_MODE", "auto");
|
||||
}
|
||||
if std::env::var("NYASH_BOX_FACTORY_POLICY").is_err() {
|
||||
cmd.env("NYASH_BOX_FACTORY_POLICY", "builtin_first");
|
||||
}
|
||||
if std::env::var("HAKO_STAGEB_APPLY_USINGS").is_err() {
|
||||
cmd.env("HAKO_STAGEB_APPLY_USINGS", "1");
|
||||
}
|
||||
if std::env::var("NYASH_ENABLE_USING").is_err() {
|
||||
cmd.env("NYASH_ENABLE_USING", "1");
|
||||
}
|
||||
if std::env::var("HAKO_ENABLE_USING").is_err() {
|
||||
cmd.env("HAKO_ENABLE_USING", "1");
|
||||
}
|
||||
if std::env::var("NYASH_PARSER_STAGE3").is_err() {
|
||||
cmd.env("NYASH_PARSER_STAGE3", "1");
|
||||
}
|
||||
if std::env::var("HAKO_PARSER_STAGE3").is_err() {
|
||||
cmd.env("HAKO_PARSER_STAGE3", "1");
|
||||
}
|
||||
if std::env::var("HAKO_STAGEB_MODULES_LIST").is_err() {
|
||||
if let Some(mods) = Self::collect_modules_list() {
|
||||
cmd.env("HAKO_STAGEB_MODULES_LIST", mods);
|
||||
}
|
||||
}
|
||||
if std::env::var("NYASH_ENTRY").is_err() {
|
||||
cmd.env("NYASH_ENTRY", &entry_fn);
|
||||
}
|
||||
if std::env::var("STAGE1_BACKEND").is_err() {
|
||||
if let Some(be) = stage1_args
|
||||
.windows(2)
|
||||
.find(|w| w[0] == "--backend")
|
||||
.map(|w| w[1].clone())
|
||||
{
|
||||
cmd.env("STAGE1_BACKEND", be);
|
||||
}
|
||||
}
|
||||
|
||||
crate::cli_v!(
|
||||
"[stage1-cli] delegating to stub: {} -- {}",
|
||||
entry,
|
||||
stage1_args.join(" ")
|
||||
);
|
||||
let status = match cmd.status() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("[stage1-cli] failed to spawn stub: {}", e);
|
||||
return Some(97);
|
||||
}
|
||||
};
|
||||
Some(status.code().unwrap_or(1))
|
||||
}
|
||||
}
|
||||
165
tools/stage1_debug.sh
Normal file
165
tools/stage1_debug.sh
Normal file
@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env bash
|
||||
# stage1_debug.sh — Stage‑1 CLI デバッグ用ヘルパー
|
||||
#
|
||||
# 目的:
|
||||
# - Stage0(Rust)→ Stage1 CLI stub(stage1_cli.hako)の経路を
|
||||
# 再現性のある形で叩き、関連する環境変数を一括で診断する。
|
||||
# - 「いまどのフラグが効いていて、何が不足しているか」を
|
||||
# 1 コマンドで見えるようにする。
|
||||
#
|
||||
# 現状の方針(2025-11 時点):
|
||||
# - 既存の NYASH_USE_STAGE1_CLI / STAGE1_EMIT_PROGRAM_JSON などを
|
||||
# 内部でセットしつつ、将来の NYASH_STAGE1_MODE などへの移行を見据えた
|
||||
# 薄いラッパとして実装する。
|
||||
#
|
||||
# 使い方:
|
||||
# tools/stage1_debug.sh <source.hako>
|
||||
# tools/stage1_debug.sh --mode emit-program-json <source.hako>
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE' >&2
|
||||
Usage: tools/stage1_debug.sh [--mode <mode>] <source.hako>
|
||||
|
||||
Modes (current stub):
|
||||
emit-program-json : Stage-1 CLI で Program(JSON v0) を emit
|
||||
emit-mir-json : Stage-1 CLI で MIR(JSON) を emit
|
||||
run-vm : Stage-1 CLI で vm backend 実行(予定)
|
||||
|
||||
Examples:
|
||||
tools/stage1_debug.sh apps/tests/minimal.hako
|
||||
tools/stage1_debug.sh --mode emit-mir-json apps/tests/minimal.hako
|
||||
|
||||
Note:
|
||||
- 現時点では、内部的には既存の NYASH_USE_STAGE1_CLI / STAGE1_EMIT_* を
|
||||
マッピングするだけのヘルパーです。
|
||||
- 将来 NYASH_STAGE1_MODE などが導入された際は、このスクリプトから
|
||||
そちらに橋渡しする予定です。
|
||||
USAGE
|
||||
}
|
||||
|
||||
MODE="emit-program-json"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mode)
|
||||
[[ $# -ge 2 ]] || { echo "[stage1_debug] --mode requires value" >&2; usage; exit 2; }
|
||||
MODE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "[stage1_debug] missing <source.hako>" >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
SRC="$1"
|
||||
if [[ ! -f "$SRC" ]]; then
|
||||
echo "[stage1_debug] source not found: $SRC" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Resolve nyash/hakorune binary (Stage0)
|
||||
if [[ -z "${NYASH_BIN:-}" ]]; then
|
||||
if [[ -x "$ROOT_DIR/target/release/hakorune" ]]; then
|
||||
NYASH_BIN="$ROOT_DIR/target/release/hakorune"
|
||||
else
|
||||
NYASH_BIN="$ROOT_DIR/target/release/nyash"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -x "$NYASH_BIN" ]]; then
|
||||
echo "[stage1_debug] nyash binary not found at $NYASH_BIN" >&2
|
||||
echo " build with: cargo build --release" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[stage1_debug] ROOT_DIR = $ROOT_DIR"
|
||||
echo "[stage1_debug] NYASH_BIN = $NYASH_BIN"
|
||||
echo "[stage1_debug] MODE = $MODE"
|
||||
echo "[stage1_debug] SRC = $SRC"
|
||||
echo
|
||||
echo "[stage1_debug] effective env snapshot (selected keys):"
|
||||
for k in \
|
||||
NYASH_USE_STAGE1_CLI STAGE1_EMIT_PROGRAM_JSON STAGE1_EMIT_MIR_JSON \
|
||||
STAGE1_BACKEND STAGE1_SOURCE STAGE1_PROGRAM_JSON \
|
||||
NYASH_ENABLE_USING HAKO_ENABLE_USING HAKO_STAGEB_APPLY_USINGS \
|
||||
NYASH_PARSER_STAGE3 HAKO_PARSER_STAGE3 \
|
||||
NYASH_FILEBOX_MODE NYASH_BOX_FACTORY_POLICY \
|
||||
NYASH_SCRIPT_ARGS_JSON NYASH_CLI_VERBOSE STAGE1_CLI_DEBUG \
|
||||
NYASH_STAGE1_MODE NYASH_STAGE1_INPUT NYASH_FEATURES
|
||||
do
|
||||
v="${!k-}"
|
||||
if [[ -n "$v" ]]; then
|
||||
printf " %-28s= %s\n" "$k" "$v"
|
||||
fi
|
||||
done
|
||||
echo
|
||||
|
||||
# Map logical MODE → current env toggles
|
||||
case "$MODE" in
|
||||
emit-program-json)
|
||||
export NYASH_USE_STAGE1_CLI=1
|
||||
export STAGE1_EMIT_PROGRAM_JSON=1
|
||||
unset STAGE1_EMIT_MIR_JSON
|
||||
export STAGE1_BACKEND="${STAGE1_BACKEND:-vm}"
|
||||
;;
|
||||
emit-mir-json)
|
||||
export NYASH_USE_STAGE1_CLI=1
|
||||
export STAGE1_EMIT_MIR_JSON=1
|
||||
unset STAGE1_EMIT_PROGRAM_JSON
|
||||
export STAGE1_BACKEND="${STAGE1_BACKEND:-vm}"
|
||||
;;
|
||||
run-vm)
|
||||
export NYASH_USE_STAGE1_CLI=1
|
||||
unset STAGE1_EMIT_PROGRAM_JSON STAGE1_EMIT_MIR_JSON
|
||||
export STAGE1_BACKEND=vm
|
||||
;;
|
||||
*)
|
||||
echo "[stage1_debug] unknown mode: $MODE" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
export STAGE1_SOURCE="$SRC"
|
||||
|
||||
# Recommended debug toggles for Stage1 path
|
||||
export NYASH_CLI_VERBOSE="${NYASH_CLI_VERBOSE:-1}"
|
||||
export STAGE1_CLI_DEBUG="${STAGE1_CLI_DEBUG:-1}"
|
||||
export NYASH_ALLOW_NYASH="${NYASH_ALLOW_NYASH:-1}"
|
||||
export HAKO_ALLOW_NYASH="${HAKO_ALLOW_NYASH:-1}"
|
||||
export NYASH_PARSER_STAGE3="${NYASH_PARSER_STAGE3:-1}"
|
||||
export HAKO_PARSER_STAGE3="${HAKO_PARSER_STAGE3:-1}"
|
||||
export NYASH_ENABLE_USING="${NYASH_ENABLE_USING:-1}"
|
||||
export HAKO_ENABLE_USING="${HAKO_ENABLE_USING:-1}"
|
||||
|
||||
echo "[stage1_debug] running Stage1 CLI via Stage0..."
|
||||
echo " $NYASH_BIN $SRC"
|
||||
echo
|
||||
|
||||
set +e
|
||||
"$NYASH_BIN" "$SRC"
|
||||
rc=$?
|
||||
set -e
|
||||
|
||||
echo
|
||||
echo "[stage1_debug] exit code: $rc"
|
||||
exit "$rc"
|
||||
141
tools/stage1_minimal.sh
Normal file
141
tools/stage1_minimal.sh
Normal file
@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env bash
|
||||
# stage1_minimal.sh — Stage‑1 CLI 経路の最小実行ヘルパー
|
||||
#
|
||||
# 目的:
|
||||
# - 「環境変数をあまり意識せずに」 Stage‑1 CLI 経路で
|
||||
# emit program-json / emit mir-json / run-vm を叩くための
|
||||
# ごく薄いラッパ。
|
||||
# - 中身は既存の NYASH_USE_STAGE1_CLI / STAGE1_EMIT_* に
|
||||
# マッピングしているが、外形としては
|
||||
# NYASH_STAGE1_MODE / NYASH_STAGE1_INPUT / NYASH_FEATURES
|
||||
# の 3 変数イメージで扱えるようにしておく。
|
||||
#
|
||||
# 使い方(暫定仕様):
|
||||
# NYASH_STAGE1_MODE=emit-program-json \
|
||||
# NYASH_STAGE1_INPUT=apps/tests/minimal.hako \
|
||||
# ./tools/stage1_minimal.sh
|
||||
#
|
||||
# # 省略時デフォルト:
|
||||
# # MODE = emit-program-json
|
||||
# # INPUT = 第1引数 or 必須
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE' >&2
|
||||
Usage: tools/stage1_minimal.sh [<source.hako>]
|
||||
|
||||
Environment (logical vars):
|
||||
NYASH_STAGE1_MODE : emit-program-json | emit-mir-json | run-vm (default: emit-program-json)
|
||||
NYASH_STAGE1_INPUT : source.hako path(未設定なら第1引数)
|
||||
NYASH_FEATURES : カンマ区切り using,parser-stage3,plugins など(暫定)
|
||||
|
||||
Examples:
|
||||
NYASH_STAGE1_MODE=emit-program-json \
|
||||
NYASH_STAGE1_INPUT=apps/tests/minimal.hako \
|
||||
./tools/stage1_minimal.sh
|
||||
|
||||
./tools/stage1_minimal.sh apps/tests/minimal.hako
|
||||
# → MODE=emit-program-json, INPUT=apps/tests/minimal.hako で実行
|
||||
USAGE
|
||||
}
|
||||
|
||||
MODE="${NYASH_STAGE1_MODE:-emit-program-json}"
|
||||
INPUT="${NYASH_STAGE1_INPUT:-}"
|
||||
|
||||
if [[ $# -ge 1 && -z "$INPUT" ]]; then
|
||||
INPUT="$1"
|
||||
fi
|
||||
|
||||
if [[ -z "$INPUT" ]]; then
|
||||
echo "[stage1_minimal] missing NYASH_STAGE1_INPUT or <source.hako>" >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$INPUT" ]]; then
|
||||
echo "[stage1_minimal] source not found: $INPUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Resolve nyash/hakorune binary
|
||||
if [[ -z "${NYASH_BIN:-}" ]]; then
|
||||
if [[ -x "$ROOT_DIR/target/release/hakorune" ]]; then
|
||||
NYASH_BIN="$ROOT_DIR/target/release/hakorune"
|
||||
else
|
||||
NYASH_BIN="$ROOT_DIR/target/release/nyash"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -x "$NYASH_BIN" ]]; then
|
||||
echo "[stage1_minimal] nyash binary not found at $NYASH_BIN" >&2
|
||||
echo " build with: cargo build --release" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[stage1_minimal] ROOT_DIR = $ROOT_DIR"
|
||||
echo "[stage1_minimal] NYASH_BIN = $NYASH_BIN"
|
||||
echo "[stage1_minimal] MODE = $MODE"
|
||||
echo "[stage1_minimal] INPUT = $INPUT"
|
||||
|
||||
# Map logical flags to current envs
|
||||
export STAGE1_SOURCE="$INPUT"
|
||||
export NYASH_USE_STAGE1_CLI=1
|
||||
|
||||
case "$MODE" in
|
||||
emit-program-json)
|
||||
export STAGE1_EMIT_PROGRAM_JSON=1
|
||||
unset STAGE1_EMIT_MIR_JSON
|
||||
export STAGE1_BACKEND="${STAGE1_BACKEND:-vm}"
|
||||
;;
|
||||
emit-mir-json)
|
||||
export STAGE1_EMIT_MIR_JSON=1
|
||||
unset STAGE1_EMIT_PROGRAM_JSON
|
||||
export STAGE1_BACKEND="${STAGE1_BACKEND:-vm}"
|
||||
;;
|
||||
run-vm)
|
||||
unset STAGE1_EMIT_PROGRAM_JSON STAGE1_EMIT_MIR_JSON
|
||||
export STAGE1_BACKEND=vm
|
||||
;;
|
||||
*)
|
||||
echo "[stage1_minimal] unknown NYASH_STAGE1_MODE: $MODE" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
# nyash_debug (統合案) は現状未使用。従来どおり CLI_VERBOSE/STAGE1_CLI_DEBUG を直接見る。
|
||||
export NYASH_CLI_VERBOSE=1
|
||||
export STAGE1_CLI_DEBUG="${STAGE1_CLI_DEBUG:-1}"
|
||||
|
||||
# Features (暫定: using / parser-stage3 / plugins のみ見ておく)
|
||||
FEATURES="${NYASH_FEATURES:-}"
|
||||
if [[ "$FEATURES" == *"using"* ]]; then
|
||||
export NYASH_ENABLE_USING=1
|
||||
export HAKO_ENABLE_USING=1
|
||||
fi
|
||||
if [[ "$FEATURES" == *"parser-stage3"* ]]; then
|
||||
export NYASH_PARSER_STAGE3=1
|
||||
export HAKO_PARSER_STAGE3=1
|
||||
fi
|
||||
if [[ "$FEATURES" == *"plugins"* ]]; then
|
||||
export NYASH_DISABLE_PLUGINS=0
|
||||
else
|
||||
# デフォルトは安全側: plugin許可だが config に委ねる
|
||||
: # ここでは特に上書きしない
|
||||
fi
|
||||
|
||||
echo "[stage1_minimal] executing via Stage1 CLI..."
|
||||
echo " $NYASH_BIN $INPUT"
|
||||
echo
|
||||
|
||||
set +e
|
||||
"$NYASH_BIN" "$INPUT"
|
||||
rc=$?
|
||||
set -e
|
||||
|
||||
echo
|
||||
echo "[stage1_minimal] exit code: $rc"
|
||||
exit "$rc"
|
||||
Reference in New Issue
Block a user