🚀 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:
@ -311,6 +311,25 @@ box ParserBox {
|
||||
me.gpos_set(j)
|
||||
return ""
|
||||
}
|
||||
// simple assignment: IDENT '=' expr ; → JSON v0 Local{name, expr} (Stage‑2 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}}"
|
||||
}
|
||||
|
||||
12
apps/tests/loop_phi_one_sided.nyash
Normal file
12
apps/tests/loop_phi_one_sided.nyash
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
18
apps/tests/nested_loop_inner_break_isolated.nyash
Normal file
18
apps/tests/nested_loop_inner_break_isolated.nyash
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
18
apps/tests/nested_loop_inner_continue_isolated.nyash
Normal file
18
apps/tests/nested_loop_inner_continue_isolated.nyash
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
9
apps/tests/shortcircuit_and_phi_skip.nyash
Normal file
9
apps/tests/shortcircuit_and_phi_skip.nyash
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
13
apps/tests/shortcircuit_nested_selective_assign.nyash
Normal file
13
apps/tests/shortcircuit_nested_selective_assign.nyash
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
9
apps/tests/shortcircuit_or_phi_skip.nyash
Normal file
9
apps/tests/shortcircuit_or_phi_skip.nyash
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
6
apps/tests/string_method_chain.nyash
Normal file
6
apps/tests/string_method_chain.nyash
Normal file
@ -0,0 +1,6 @@
|
||||
static box Main {
|
||||
main(args) {
|
||||
return "abcd".substring(1,3).length()
|
||||
}
|
||||
}
|
||||
|
||||
15
apps/tests/try_catch_finally_no_throw.nyash
Normal file
15
apps/tests/try_catch_finally_no_throw.nyash
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
apps/tests/try_finally_break_inner_loop.nyash
Normal file
22
apps/tests/try_finally_break_inner_loop.nyash
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
24
apps/tests/try_finally_continue_inner_loop.nyash
Normal file
24
apps/tests/try_finally_continue_inner_loop.nyash
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
13
apps/tests/try_finally_normal.nyash
Normal file
13
apps/tests/try_finally_normal.nyash
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
apps/tests/try_finally_return_override.nyash
Normal file
11
apps/tests/try_finally_return_override.nyash
Normal file
@ -0,0 +1,11 @@
|
||||
static box Main {
|
||||
main(args) {
|
||||
// try 内の return より finally の return を優先(現行仕様)
|
||||
try {
|
||||
return 1
|
||||
} finally {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user