core: for/foreach -> Loop normalization (always-on); LoopForm MVP-3 per-segment reorder; smokes stabilized (VM + LLVM PHI); docs updated (macro-system, loopform); quiet macro load logs

This commit is contained in:
Selfhosting Dev
2025-09-20 08:39:40 +09:00
parent f50f79994f
commit 8a84339ac2
20 changed files with 1216 additions and 32 deletions

View File

@ -92,6 +92,21 @@ while (i < n) {
- break: 「現キャリア」を exit へ(ヘッダ合流と衝突しないよう保持)。
- いずれも 1 段ネストまでの最小対応から開始。
MVP-3実装済み・最小対応
- 本体を break/continue でセグメント分割し、各セグメント内のみ安全に「非代入→代入」に整列。
- ガード:
- 代入先は変数のみ(フィールド等は対象外)
- 全体の更新変数は最大2種MVP-2 制約を継承)
- セグメント内で「代入の後に非代入」があれば整列しない(順序保持)
- スモーク:
- `tools/test/smoke/macro/loopform_continue_break_output_smoke.sh`
for / foreach の糖衣と正規化(概要)
- for: `for(fn(){ init }, cond, fn(){ step }, fn(){ body })``init; loop(cond){ body; step }` へ正規化。
- init/step は `Assignment`/`Local` 単体でも可。
- foreach: `foreach(arr, "x", fn(){ body })``__ny_i` で走査する Loop へ正規化し、`x``arr.get(__ny_i)` に置換。
- スモーク: `tools/test/smoke/macro/for_foreach_output_smoke.sh`
対応状況MVP→順次拡張
- Week1: whilebreak/continue無し
- Week2: break/continue/ネスト最小対応、キャリア自動抽出
@ -103,13 +118,23 @@ while (i < n) {
検証
- macrogolden展開後ASTのゴールデン
- LLVM PHI健全性スモーク空PHI無し、先頭グループ化
- 出力一致スモークtwovars の実行出力が同一であること)
手元での確認
- ゴールデン(キー順無視の比較)
- `tools/test/golden/macro/loop_simple_user_macro_golden.sh`
- `tools/test/golden/macro/loop_two_vars_user_macro_golden.sh`
- 出力一致スモークVM
- `tools/test/smoke/macro/loop_two_vars_output_smoke.sh`
- 自己ホスト前展開PyVM 経由)
- `NYASH_VM_USE_PY=1 NYASH_USE_NY_COMPILER=1 NYASH_MACRO_ENABLE=1 NYASH_MACRO_PATHS=apps/macros/examples/loop_normalize_macro.nyash ./target/release/nyash --macro-preexpand --backend vm apps/tests/macro/loopform/simple.nyash`
実装メモ(内蔵変換ルート / Rust
- 既定のマクロ実行は internalchildRust内蔵です。LoopNormalize は以下の保守的なガードで正規化します。
- トップレベル本体に Break/Continue がないこと
- 代入対象は最大2変数、かつ単純な変数フィールド代入などは除外
- 代入の後ろに非代入が現れない(安全に末尾整列できる)
- 条件を満たす場合のみ「非代入→代入」の順でボディを再構成します(意味は不変)。
参考
- docs/development/roadmap/phases/phase-17-loopform-selfhost/

View File

@ -60,6 +60,26 @@ NYASH_MACRO_ENABLE=1 NYASH_TEST_ARGS_DEFAULTS=1 \
```
Shows pre/post expansion AST (debug only).
## Core Normalization (always-on when macros enabled)
Certain language sugars are normalized before MIR across all runners when the macro gate is enabled. These are not user macros and do not require any registration:
- for sugar → Loop
- `for(fn(){ init }, cond, fn(){ step }, fn(){ body })`
- Emits: `init; loop(cond){ body; step }`
- `init/step` also accept a single Assignment or Local instead of `fn(){..}`.
- foreach sugar → Loop
- `foreach(array_expr, "x", fn(){ body_with_x })`
- Expands into an index-based loop with `__ny_i`, and substitutes `x` with `array_expr.get(__ny_i)` inside body.
Normalization order (within the core pass):
1) for/foreach → 2) match(PeekExpr) → 3) loop tail alignment (carrier-like ordering; break/continue segments supported).
Notes:
- Backward-compat function names `ny_for` / `ny_foreach` are also accepted but `for` / `foreach` are preferred.
- This pass is part of the language pipeline; it is orthogonal to user-defined macros.
## Developer API (preview)
- Pattern/Quote primitives are available to bootstrap macro authorship.