debug(stageb): センチネル追加でVMバグ特定 - using経由static box内loop不実行

## 🔍 調査結果
###  確認事項
- **本物の実装が呼ばれている**: SENTINEL出力で確認済み
  - 🔥 SENTINEL_SKIP_WS_CALLED!!!
  - 🎯 SENTINEL_KW_BOUNDARY_BEFORE_CALLED!!!
  - 🎯 SENTINEL_KW_BOUNDARY_AFTER_CALLED!!!
  - 🔤 SENTINEL_IS_IDENT_CHAR_CALLED!!!

### 🐛 重大バグ発見
**症状**: `FuncScannerBox.skip_whitespace` 内の `loop(1 == 1)` が実行されない

**証拠**:
```
[skip_ws] START idx=10 s.length()=173
[skip_ws] i=10 n=173
[skip_ws] RETURN i=10  ← ループボディが実行されず即座にreturn
```
- `[skip_ws] LOOP-TOP i=10` が**一度も出力されない**
- loop(1 == 1) の無限ループすら実行されない

**影響範囲**:
- box_name抽出失敗(空文字列)
- defs生成失敗(defs_len=0)
- canary テスト失敗

**問題の本質**:
- using 経由で読み込まれたモジュールの static box 内
- 静的メソッド呼び出し (`FuncScannerBox.skip_whitespace(...)`)
- loop 構文が VM/MIR レベルで実行されない

## 🔧 修正内容
1. **センチネル追加**: 4箇所に明確な出力追加
   - skip_whitespace, kw_boundary_before, kw_boundary_after, is_ident_char
2. **呼び出し修正**: `me.scan_all_boxes` → `StageBFuncScannerBox.scan_all_boxes`

## 📊 次のステップ
VM/MIR レイヤーでの loop 構文実装確認が必要

🤖 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-20 08:03:05 +09:00
parent 54b2735f13
commit 0dc8510daf
2 changed files with 19 additions and 2 deletions

View File

@ -1214,7 +1214,7 @@ static box StageBDriverBox {
tracer.log("StageBDriverBox.main:func_scan src_head=" + head) tracer.log("StageBDriverBox.main:func_scan src_head=" + head)
} }
local methods = me.scan_all_boxes(src) local methods = StageBFuncScannerBox.scan_all_boxes(src)
{ {
local cnt = 0 local cnt = 0

View File

@ -83,13 +83,18 @@ static box FuncScannerBox {
} else { } else {
if i + 3 <= n && s.substring(i, i + 3) == "box" && FuncScannerBox.kw_boundary_before(s, i) == 1 && FuncScannerBox.kw_boundary_after(s, i + 3) == 1 { if i + 3 <= n && s.substring(i, i + 3) == "box" && FuncScannerBox.kw_boundary_before(s, i) == 1 && FuncScannerBox.kw_boundary_after(s, i + 3) == 1 {
local cursor = i + 3 local cursor = i + 3
print("[DEBUG] box keyword found at i=" + ("" + i))
cursor = FuncScannerBox.skip_whitespace(s, cursor) cursor = FuncScannerBox.skip_whitespace(s, cursor)
print("[DEBUG] after skip_whitespace: cursor=" + ("" + cursor))
local name_start = cursor local name_start = cursor
loop(cursor < n) { loop(cursor < n) {
local ch_name = s.substring(cursor, cursor + 1) local ch_name = s.substring(cursor, cursor + 1)
print("[DEBUG] cursor=" + ("" + cursor) + " ch_name='" + ch_name + "' is_ident=" + ("" + FuncScannerBox.is_ident_char(ch_name)))
if FuncScannerBox.is_ident_char(ch_name) == 1 { cursor = cursor + 1 } else { break } if FuncScannerBox.is_ident_char(ch_name) == 1 { cursor = cursor + 1 } else { break }
} }
print("[DEBUG] name_start=" + ("" + name_start) + " cursor=" + ("" + cursor) + " n=" + ("" + n))
local box_name = s.substring(name_start, cursor) local box_name = s.substring(name_start, cursor)
print("[DEBUG] box_name='" + box_name + "' len=" + ("" + box_name.length()))
if box_name == "" { if box_name == "" {
next_i = cursor next_i = cursor
} else { } else {
@ -302,18 +307,28 @@ static box FuncScannerBox {
// Helper: 空白文字space/tab/newline/CRを idx 位置からスキップ // Helper: 空白文字space/tab/newline/CRを idx 位置からスキップ
// 戻り値: スキップ後の位置(空白でない文字の位置、または文字列末尾) // 戻り値: スキップ後の位置(空白でない文字の位置、または文字列末尾)
method skip_whitespace(s, idx) { method skip_whitespace(s, idx) {
print("🔥🔥🔥 SENTINEL_SKIP_WS_CALLED!!! 🔥🔥🔥")
print("[skip_ws] START idx=" + ("" + idx) + " s.length()=" + ("" + s.length()))
local i = idx local i = idx
local n = s.length() local n = s.length()
loop(i < n) { print("[skip_ws] i=" + ("" + i) + " n=" + ("" + n))
// WORKAROUND: Changed from loop(i < n) to loop with internal if check
// Original: loop(i < n) { ... } was not executing body even when condition was true!
loop(1 == 1) {
print("[skip_ws] LOOP-TOP i=" + ("" + i))
if i >= n { break }
local ch = s.substring(i, i + 1) local ch = s.substring(i, i + 1)
print("[skip_ws] LOOP i=" + ("" + i) + " ch='" + ch + "'")
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { i = i + 1 } else { break } if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { i = i + 1 } else { break }
} }
print("[skip_ws] RETURN i=" + ("" + i))
return i return i
} }
// Helper: キーワード前の境界チェック("box" などが識別子の一部でないことを確認) // Helper: キーワード前の境界チェック("box" などが識別子の一部でないことを確認)
// 戻り値: 1=境界OKキーワードとして有効, 0=境界NG識別子の一部 // 戻り値: 1=境界OKキーワードとして有効, 0=境界NG識別子の一部
method kw_boundary_before(s, idx) { method kw_boundary_before(s, idx) {
print("🎯 SENTINEL_KW_BOUNDARY_BEFORE_CALLED!!!")
if idx <= 0 { return 1 } if idx <= 0 { return 1 }
local ch = s.substring(idx - 1, idx) local ch = s.substring(idx - 1, idx)
if FuncScannerBox.is_ident_char(ch) == 1 { return 0 } if FuncScannerBox.is_ident_char(ch) == 1 { return 0 }
@ -323,6 +338,7 @@ static box FuncScannerBox {
// Helper: キーワード後の境界チェック("box" などが識別子の一部でないことを確認) // Helper: キーワード後の境界チェック("box" などが識別子の一部でないことを確認)
// 戻り値: 1=境界OKキーワードとして有効, 0=境界NG識別子の一部 // 戻り値: 1=境界OKキーワードとして有効, 0=境界NG識別子の一部
method kw_boundary_after(s, idx) { method kw_boundary_after(s, idx) {
print("🎯 SENTINEL_KW_BOUNDARY_AFTER_CALLED!!!")
if idx >= s.length() { return 1 } if idx >= s.length() { return 1 }
local ch = s.substring(idx, idx + 1) local ch = s.substring(idx, idx + 1)
if FuncScannerBox.is_ident_char(ch) == 1 { return 0 } if FuncScannerBox.is_ident_char(ch) == 1 { return 0 }
@ -332,6 +348,7 @@ static box FuncScannerBox {
// Helper: 識別子として有効な文字かチェック0-9, A-Z, a-z, _ // Helper: 識別子として有効な文字かチェック0-9, A-Z, a-z, _
// 戻り値: 1=識別子文字, 0=非識別子文字 // 戻り値: 1=識別子文字, 0=非識別子文字
method is_ident_char(ch) { method is_ident_char(ch) {
print("🔤 SENTINEL_IS_IDENT_CHAR_CALLED!!!")
if ch == null || ch == "" { return 0 } if ch == null || ch == "" { return 0 }
if ch >= "0" && ch <= "9" { return 1 } if ch >= "0" && ch <= "9" { return 1 }
if ch >= "A" && ch <= "Z" { return 1 } if ch >= "A" && ch <= "Z" { return 1 }