🚀 Break/Continue/Try-Catch構文のサポート追加とMIRループ制御強化

## 主な変更点

### 🎯 MIRループ制御の実装(根治対応)
- src/mir/loop_builder.rs: Break/Continue対応のループコンテキスト管理
  - ループのbreak/continueターゲットブロック追跡
  - ネストループの適切な処理
- src/mir/builder.rs: Break/Continue文のMIR生成実装
- src/tokenizer.rs: Break/Continue/Tryトークン認識追加

### 📝 セルフホストパーサーの拡張
- apps/selfhost-compiler/boxes/parser_box.nyash:
  - Stage-3: break/continue構文受理(no-op実装)
  - Stage-3: try-catch-finally構文受理(構文解析のみ)
  - エラー処理構文の将来対応準備

### 📚 ドキュメント更新
- 論文K(爆速事件簿): 45事例に更新(4件追加)
  - PyVM迂回路の混乱事件
  - Break/Continue無限ループ事件
  - EXE-first戦略の再発見
- 論文I(開発秘話): Day 38の重要決定追加

### 🧪 テストケース追加
- apps/tests/: ループ制御とPHIのテストケース
  - nested_loop_inner_break_isolated.nyash
  - nested_loop_inner_continue_isolated.nyash
  - loop_phi_one_sided.nyash
  - shortcircuit関連テスト

## 技術的詳細
- Break/ContinueをMIRレベルで適切に処理
- 無限ループ問題(CPU 99.9%暴走)の根本解決
- 将来の例外処理機能への準備

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Selfhosting Dev
2025-09-15 22:14:42 +09:00
parent d90216e9c4
commit 94d95dfbcd
34 changed files with 989 additions and 37 deletions

View File

@ -311,6 +311,25 @@ box ParserBox {
me.gpos_set(j)
return ""
}
// simple assignment: IDENT '=' expr ; → JSON v0 Local{name, expr} (Stage2 uses Local for updates)
if me.is_alpha(src.substring(j, j+1)) {
local idp0 = me.read_ident2(src, j)
local at0 = idp0.lastIndexOf("@")
if at0 > 0 {
local name0 = idp0.substring(0, at0)
local k0 = me.to_int(idp0.substring(at0+1, idp0.length()))
k0 = me.skip_ws(src, k0)
if src.substring(k0, k0+1) == "=" {
k0 = k0 + 1
k0 = me.skip_ws(src, k0)
local e0 = me.parse_expr2(src, k0)
k0 = me.gpos_get()
if k0 <= stmt_start { if k0 < src.length() { k0 = k0 + 1 } else { k0 = src.length() } }
me.gpos_set(k0)
return "{\"type\":\"Local\",\"name\":\"" + name0 + "\",\"expr\":" + e0 + "}"
}
}
}
if me.starts_with_kw(src, j, "return") == 1 {
j = j + 6
j = me.skip_ws(src, j)
@ -454,3 +473,70 @@ box ParserBox {
}
static box ParserStub { main(args) { return 0 } }
// Stage-3 acceptance (syntax only): break / continue → no-op expression
if me.starts_with_kw(src, j, "break") == 1 {
j = j + 5
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
me.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
}
if me.starts_with_kw(src, j, "continue") == 1 {
j = j + 8
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
me.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
}
// Stage-3 acceptance: throw expr → degrade to Expr(expr)
if me.starts_with_kw(src, j, "throw") == 1 {
j = j + 5
j = me.skip_ws(src, j)
local e_throw = me.parse_expr2(src, j)
j = me.gpos_get()
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
me.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":" + e_throw + "}"
}
// Stage-3 acceptance: try { ... } (catch ...)* (finally { ... })? → degrade to no-op (syntax only)
if me.starts_with_kw(src, j, "try") == 1 {
j = j + 3
j = me.skip_ws(src, j)
// parse try block
local try_res = me.parse_block2(src, j)
local at_t = try_res.lastIndexOf("@")
j = me.to_int(try_res.substring(at_t+1, try_res.length()))
// zero or more catch
local guard_ct = 0
local max_ct = 100
local cont_ct = 1
loop(cont_ct == 1) {
if guard_ct > max_ct { cont_ct = 0 } else { guard_ct = guard_ct + 1 }
j = me.skip_ws(src, j)
if me.starts_with_kw(src, j, "catch") == 1 {
j = j + 5
j = me.skip_ws(src, j)
if src.substring(j, j+1) == "(" { j = j + 1 j = me.skip_ws(src, j)
// optional type + name
if me.is_alpha(src.substring(j, j+1)) { local id1 = me.read_ident2(src, j) local at1 = id1.lastIndexOf("@") j = me.to_int(id1.substring(at1+1, id1.length())) j = me.skip_ws(src, j) }
if me.is_alpha(src.substring(j, j+1)) { local id2 = me.read_ident2(src, j) local at2 = id2.lastIndexOf("@") j = me.to_int(id2.substring(at2+1, id2.length())) j = me.skip_ws(src, j) }
if src.substring(j, j+1) == ")" { j = j + 1 }
}
j = me.skip_ws(src, j)
// catch body
local c_res = me.parse_block2(src, j)
local atc = c_res.lastIndexOf("@")
j = me.to_int(c_res.substring(atc+1, c_res.length()))
} else { cont_ct = 0 }
}
// optional finally
j = me.skip_ws(src, j)
if me.starts_with_kw(src, j, "finally") == 1 {
j = j + 7
j = me.skip_ws(src, j)
local f_res = me.parse_block2(src, j)
local atf = f_res.lastIndexOf("@")
j = me.to_int(f_res.substring(atf+1, f_res.length()))
}
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
me.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
}

View File

@ -0,0 +1,12 @@
static box Main {
main(args) {
local i = 0
local sum = 0
loop(i < 5) {
if (i % 2 == 1) { sum = sum + i } else { }
i = i + 1
}
return sum
}
}

View File

@ -0,0 +1,18 @@
static box Main {
main(args) {
local i = 0
local s = 0
loop(i < 3) {
local j = 0
loop(j < 4) {
j = j + 1
if (j == 3) { break }
s = s + 1
}
i = i + 1
}
// outer=3 回、各回で j=1,2 のみ加算 → 2×3=6
return s
}
}

View File

@ -0,0 +1,18 @@
static box Main {
main(args) {
local i = 0
local s = 0
loop(i < 2) {
local j = 0
loop(j < 3) {
j = j + 1
if (j == 1) { continue }
s = s + 1
}
i = i + 1
}
// outer=2 回、各回で j=2,3 のみ加算 → 2×2=4
return s
}
}

View File

@ -0,0 +1,9 @@
static box Main {
main(args) {
local x = 0
((x = x + 1) < 0) && ((x = x + 1) < 0)
// LHS が false → RHS は評価されず x は 1 のまま
return x
}
}

View File

@ -0,0 +1,13 @@
static box Main {
main(args) {
local x = 0
local a = true
local b = false
// a && (b || ((x = 1) > 0))
if (a && (b || ((x = 1) > 0))) {
// x は 1 に設定済み
} else { }
return x
}
}

View File

@ -0,0 +1,9 @@
static box Main {
main(args) {
local x = 0
((x = x + 1) >= 0) || ((x = x + 1) < 0)
// LHS が true → RHS は評価されず x は 1 のまま
return x
}
}

View File

@ -0,0 +1,6 @@
static box Main {
main(args) {
return "abcd".substring(1,3).length()
}
}

View File

@ -0,0 +1,15 @@
static box Main {
main(args) {
local console = new ConsoleBox()
try {
console.println("T")
return 10
} catch (Error e) {
// 例外なし → ここは通らない想定
console.println("C")
} finally {
console.println("F")
}
}
}

View File

@ -0,0 +1,22 @@
static box Main {
main(args) {
local i = 0
local fin = 0
loop(i < 3) {
try {
local j = 0
loop(j < 5) {
if (j == 2) { break }
j = j + 1
}
} finally {
// inner の break に関わらず finally は 1 回実行
fin = fin + 1
}
i = i + 1
}
// outer 反復回数 = 3
return fin
}
}

View File

@ -0,0 +1,24 @@
static box Main {
main(args) {
local fin = 0
local i = 0
loop(i < 2) {
local j = 0
loop(j < 3) {
// 各反復で continue が起きたかを印付け
local mark = 0
try {
j = j + 1
if (j == 1) { mark = 1; continue }
} finally {
// continue でも finally は実行される
if (mark == 1) { fin = fin + 1 }
}
}
i = i + 1
}
// outer=2 回、各回で j==1 の continue が 1 度発生 → fin=2
return fin
}
}

View File

@ -0,0 +1,13 @@
static box Main {
main(args) {
local console = new ConsoleBox()
try {
console.println("T")
return 123
} finally {
// finally は必ず実行される想定
console.println("F")
}
}
}

View File

@ -0,0 +1,11 @@
static box Main {
main(args) {
// try 内の return より finally の return を優先(現行仕様)
try {
return 1
} finally {
return 2
}
}
}