feat(stageb): Phase 25.1c - Stage-B トレース追加(dev-only)
追加内容:
- StageBTraceBox: dev トレース用 Box 追加(HAKO_STAGEB_TRACE=1 で有効)
- トレースポイント:
- StageBArgsBox.resolve_src: enter/return_len
- StageBBodyExtractorBox.build_body_src: enter_len/return_len
- StageBDriverBox.main: enter/after_resolve_src/after_build_body_src/
after_parse_program2/func_scan methods/exit rc=0
Phase 25.1c 目標:
- Stage-B / Stage-1 CLI 構造デバッグ
- fib canary / selfhost CLI canary の rc=1 原因特定
ポリシー:
- dev env でガード(挙動不変)
- 既定挙動は変更せず、観測のみ追加
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -31,6 +31,60 @@ Update (2025-11-18 — Phase 25.1k: LoopSSA v2 (.hako) & Stage‑B harness 追
|
||||
- K‑C: PhiInjectorBox に Carrier/Pinned ベースの v2 入口を追加(既存 `_collect_phi_vars` は当面維持)。
|
||||
- K‑D: Stage‑B Test3 の `%0` SSA 問題が悪化していないことを確認しつつ、LoopSSA 有効/無効の切り分けを残す。
|
||||
|
||||
Update (2025-11-18 — Phase 25.1b: Stage‑B/Ny selfhost parser depth guards hotfix)
|
||||
- Context:
|
||||
- Stage‑B / Ny selfhost ラインでは、`ParserBox.parse_program2` → `ParserStmtBox.parse` → `ParserControlBox.*` の再帰ガードに `HAKO_STAGEB_*_DEPTH` 環境変数を使っており、誤ループや自己再帰を検出する設計になっている。
|
||||
- ただしこれまでの実装では、正常終了パスで depth を 0 に戻していない箇所があり、一度ガードが立つとその後のすべての呼び出しが「再帰検出扱い」になってしまい、空文列 `""` やダミー Break/Continue を返すことで **進捗ガード頼みの疑似無限ループ** を引き起こしていた。
|
||||
- 特に `.ny` の大きなソースを inline selfhost 経路(`ny_selfhost_inline.sh` / `inline_selfhost_emit.hako` 相当)で処理した場合に、`ParserBox.parse_program2` が 60s 付近まで進んでから `Undefined variable: local` を出す背景の一つになっていた疑いがある。
|
||||
- Done:
|
||||
- `lang/src/compiler/parser/stmt/parser_stmt_box.hako`:
|
||||
- `ParserStmtBox.parse` の再帰ガード:
|
||||
- 入口で `HAKO_STAGEB_STMT_DEPTH` を `"1"` にセットし、すでに `"!=0"` のときは `[stageb/recursion] ParserStmtBox.parse recursion detected` を出して空文 `""` を返す構造は維持。
|
||||
- そのうえで「正常に 1 文パースして return する」すべての分岐で `env.set("HAKO_STAGEB_STMT_DEPTH", "0")` を明示的に呼ぶように修正し、**1 文=1 ガードサイクル** として閉じるようにした。
|
||||
- 対象: `@extern_c` アノテーション、`using` 文(`parse_using`)、代入文(`Local` 生成)、`return` 文、`local` 宣言(Stage‑3)、`if/loop/break/continue/throw/try` を各 Box に委譲する分岐、および既存の Expr fallback。
|
||||
- これにより、1 回目の parse 成功後に `HAKO_STAGEB_STMT_DEPTH` が 1 のまま残り、2 回目以降がすべて再帰検出ブランチに落ちるバグを解消(Stage‑B/Stage‑1/自前 Ny selfhost での multi‑stmt ソースを安定して扱えるようにした)。
|
||||
- `lang/src/compiler/parser/stmt/parser_control_box.hako`:
|
||||
- `parse_break` / `parse_continue` の Stage‑3 パスで、正常終了時にもそれぞれ `HAKO_STAGEB_BREAK_DEPTH` / `HAKO_STAGEB_CONTINUE_DEPTH` を `"0"` に戻すように修正。
|
||||
- これにより、break/continue を含むループを Stage‑3 構文でパースした後も、次の `parse_stmt2` 呼び出しからは再び通常パスに戻れるようになった(break/continue 自体は既存どおり `{type:"Break"}` / `{type:"Continue"}` を返す)。
|
||||
- Quick verification:
|
||||
- `tools/ny_selfhost_inline.sh local_tests/test_large_method_break.nyash target/release/hakorune`:
|
||||
- 実行時に Rust VM の `Invalid value: use of undefined value ValueId(..)` や無限ループは発生せず、inline selfhost パーサ経路は正常終了することを確認。
|
||||
- これにより Ny selfhost パイプラインの **「Rust VM の SSA/PHI 破綻で即死」フェーズは抜けており、残る課題は .hako 側 Stage‑B コンパイラ(構文/using/Stage‑3 周辺)の rc=1 に集約されている** ことがはっきりした。
|
||||
- Current status of Phase 25.1 selfhost canaries:
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/stageb_fib_program_defs_canary_vm.sh`:
|
||||
- 依然として Stage‑B rc=1 で FAIL(Program(JSON v0) の抽出以前で落ちている)。ログ上は GC/SSA ではなく Stage‑B 本体側のエラーとして見えている。
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/selfhost_cli_run_basic_vm.sh`:
|
||||
- `HAKO_SELFHOST_BUILDER_FIRST=1` + `HAKO_MIR_BUILDER_CLI_RUN=1` で selfhost builder を優先した場合に、Stage‑B / direct MIR emit 双方 rc=1 のまま(`HakoCli.run` の MIR(JSON) が生成されない)。
|
||||
- 代表的な Rust MIR/SSA スモーク(`mir_stage1_using_resolver_*` / `mir_loopform_exit_phi` / `mir_stageb_loop_break_continue`)および Ny selfhost inline テストでは、Rust VM の `Undefined value` / `%0` 二重定義は再現しておらず、25.1 の Rust 側 SSA/PHI 修正は安定したとみなす。
|
||||
|
||||
→ 今後の 25.1b/25.1c では、Ny selfhost/Numeric core の前に **Stage‑B (`compiler_stageb.hako`) と Stage‑1 CLI (`HakoCli.run`) の .hako 側構文/using/Stage‑3 ロジックを構造的に整理し、rc=1 の原因を 1 関数=1 テスト単位で切り出して潰していく** フェーズに入る。
|
||||
|
||||
Update (2025-11-18 — Phase 25.1c: Stage‑B / Stage‑1 CLI 構造デバッグプラン)
|
||||
- Context:
|
||||
- Phase 25.1c は `env.*` / `hostbridge.*` / `env.box_introspect.*` の責務整理に加えて、Stage‑B Main (`compiler_stageb.hako`) と Stage‑1 CLI (`HakoCli.run`) の構造を「Region/スコープが追いやすい形」に整えるフェーズとして位置づけた。
|
||||
- Rust 側ではすでに `Region / RefSlotKind / FunctionSlotRegistry + ControlForm` により Loop/If のスコープが安定しており、`NYASH_REGION_TRACE=1` で Stage‑B 由来関数の Region/slot を観測できる状態。これを .hako 側 Stage‑B 実装の「正解ビュー」として使う。
|
||||
- 一方、25.1b の depth guard hotfix により Ny selfhost パーサ/Stage‑B の `local` 系バグのうち「疑似再帰で進捗ガード頼みになる」ものは排除され、残る問題は主に Stage‑B 本体(compiler_stageb.hako)の rc=1 と CLI 経路の構造に集約されている。
|
||||
- Plan (25.1c, initial steps):
|
||||
1) Stage‑B rc=1 の発生箇所を箱単位へ縮小:
|
||||
- 代表 canary を 2 本ターゲットにする:
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/stageb_fib_program_defs_canary_vm.sh`(fib 風 defs + Loop の presence を確認する canary)。
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/selfhost_cli_run_basic_vm.sh`(HakoCli.run の selfhost MIR 生成 canary)。
|
||||
- `compiler_stageb.hako` の Stage‑B Main を「箱理論」に沿って分割した設計(`StageBArgsBox` / `StageBBodyExtractorBox` / `StageBDriverBox`)に合わせて、各箱入口/出口に `[stageb/trace:<box>.<method>:enter|leave]` を付与。
|
||||
→ どの箱のどのメソッドで rc=1 になっているかをまずログだけで特定する(挙動は変えない)。
|
||||
2) Rust Region ログと .hako 側のスコープ観測を揃える:
|
||||
- Rust 側の `src/mir/region/*` + `NYASH_REGION_TRACE=1` をオンにし、`StageBBodyExtractorBox.*` 周辺ループ/if の Region 情報(entry/exit/slots)をログとして取得する。
|
||||
- .hako 側 Stage‑B 実装に観測専用 Box(案: `StageBRegionObserverBox`)を追加し、`enter_region(kind, name, slots_json)` / `leave_region()` などの API で「箱名+制御構造+生きているローカル名」のみ JSON で出力する。
|
||||
- Stage‑B 本体ではこの Box を外側ループ・内側 if などの境界で呼び出し、Rust の `[region/observe]` ログと .hako ログを突き合わせて、「どのスコープでどのローカル(例: `body_src` / `bundle_srcs` / `require_mods`)が欠損しているか」を構造レベルで炙り出す。
|
||||
3) Stage‑B 用ミニマムハーネスを Rust / .hako 両方に用意:
|
||||
- 100〜200 行規模の最小 Hako サンプル(`using` + 1 box + 1 loop 程度)を `lang/src/compiler/tests/stageb_min_sample.hako` 相当として固定し、Rust 側では AST→MIR→`NYASH_VM_VERIFY_MIR=1` を通すテストを維持。
|
||||
- .hako 側では同じサンプルを入力とする mini driver(`StageBDriverMiniBox` のようなもの)を用意し、`StageBArgsBox.resolve_src` → `StageBBodyExtractorBox.build_body_src` → `ParserBox.parse_program2` だけを見る canary を追加。
|
||||
→ fib / HakoCli.run より軽いケースで Stage‑B の構造バグを素早く再現・修正できるようにする。
|
||||
4) Stage‑1 CLI selfhost ラインは Stage‑B 修正後に再アタック:
|
||||
- 上記 1)〜3) で Stage‑B canary(fib defs / stageb_min / mini driver)の rc=1 を解消したあとに、25.1b 側の selfhost builder / `HakoCli.run` MIR 生成 canary を再度実行し、Program(JSON v0)→MIR(JSON) の差分を Rust ラインと比較していく。
|
||||
- Goal for 25.1c:
|
||||
- Stage‑B (`compiler_stageb.hako`) と Stage‑1 CLI (`HakoCli.run`) に関する「rc=1 / Stage‑B fail」の原因を、Region/スコープ構造まで落とし込んだうえで 1 箱単位で潰せる状態にすること。
|
||||
- Rust Region レイヤを正としつつ、.hako 側でも同等の Region 観測レイヤ(RegionBox 的な構造)を持つことで、今後の GC/寿命設計や selfhost 側 LoopSSA v2 への追従を見通し良く行えるようにしておく。
|
||||
|
||||
Update (2025-11-18 — Phase 25.1l: Region/GC 観測レイヤー導入(Rust 側のみ) — completed)
|
||||
- Context:
|
||||
- LoopForm v2 / ControlForm / Conservative PHI Box により、If/Loop の SSA/PHI は Rust 側で安定しているが、
|
||||
|
||||
@ -85,3 +85,38 @@ Status: planning(構造整理フェーズ・挙動は変えない)
|
||||
- Phase 25.1b: selfhost builder / multi‑carrier / BoxTypeInspector 実装フェーズ(機能側)。
|
||||
- Phase 25.1c: そのうち「env.* / hostbridge.* / BoxIntrospect」に加えて、Stage‑B Main / LoopBuilder / builder 観測レイヤの構造と責務も整理するメタフェーズ(構造側)。
|
||||
- 挙動を変えないこと(Fail‑Fast / default path は現状維持)を前提に、小さな差分で進める。
|
||||
|
||||
### デバッグ方針(25.1c で踏みたい順番)
|
||||
|
||||
- 1) Stage‑B rc=1 の発生箇所を箱単位まで特定する
|
||||
- 代表 canary:
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/stageb_fib_program_defs_canary_vm.sh`
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/selfhost_cli_run_basic_vm.sh`
|
||||
- まずは `compiler_stageb.hako` の流れを箱ごとに分解してログする:
|
||||
- `StageBArgsBox.resolve_src`
|
||||
- `StageBBodyExtractorBox.build_body_src`
|
||||
- `ParserBox.parse_program2`
|
||||
- `FuncScannerBox.scan_all_boxes`
|
||||
- 各箱の入口/出口に `[stageb/trace:<box>.<method>:enter|leave]` のような軽いタグを置き、どの箱が rc=1 の直前で止まっているかを特定する(挙動は変えない)。
|
||||
|
||||
- 2) Rust Region レイヤを「正解ビュー」として .hako 側を寄せる
|
||||
- Rust 側にはすでに `Region / RefSlotKind / FunctionSlotRegistry + ControlForm` があり、`StageBBodyExtractorBox.*` 周辺のスロット(`src/body_src/bundle_*` など)がどの Loop/If Region に属しているかを `NYASH_REGION_TRACE=1` で観測できる。
|
||||
- 25.1c では .hako 側に観測専用 Box(案: `StageBRegionObserverBox`)を追加し、
|
||||
- `enter_region(kind, name, slots_json)`
|
||||
- `leave_region()`
|
||||
のような API で「箱名・構造名・スロット名の集合」だけを JSON で print する。
|
||||
- `StageBBodyExtractorBox` の外側ループ・内側 if など、問題になりやすい箇所でこの Box を呼び出し、Rust の `[region/observe]` ログと「木構造+スロット名」が対応しているかを確認する。
|
||||
→ ずれている Region(例: `body_src` などが早期に欠落するスコープ)から優先的に修正する。
|
||||
|
||||
- 3) Stage‑B 用の極小ハーネスを Rust / .hako 両方に用意する
|
||||
- fib canary はやや重いため、100〜200 行程度の「using + 1 box + 1 loop」だけの最小サンプルを `lang/src/compiler/tests/stageb_min_sample.hako` のようなファイルとして固定する。
|
||||
- Rust 側:
|
||||
- そのサンプルを AST→MIR に通し、`NYASH_VM_VERIFY_MIR=1` で `Undefined value` が出ないことを確認する小さなテストを用意(既存の Stage‑B 向け MIR テスト群に揃える)。
|
||||
- .hako 側:
|
||||
- 同じサンプルを入力として `StageBDriverBox` の簡易版(mini driver)を作り、`StageBArgsBox.resolve_src` → `StageBBodyExtractorBox.build_body_src` → `ParserBox.parse_program2` だけを通す driver を追加する。
|
||||
- これにより、Stage‑B/LoopBuilder に対する修正を「本番 compiler_stageb.hako 全体」ではなく「ミニマムな Hako 断片」で検証できるようにする。
|
||||
|
||||
- 4) Stage‑1 CLI (`HakoCli.run`) の selfhost ラインは Stage‑B が緑になってから扱う
|
||||
- `selfhost_cli_run_basic_vm.sh` の現状の失敗は、Stage‑B が rc=1 で Program(JSON) を 1 行も返していないことが原因であり、HakoCli.run の MIR 生成まで到達していない。
|
||||
- 25.1c ではまず Stage‑B 側(fib defs / stageb_min / mini driver)を rc=0 に戻し、
|
||||
その Program(JSON v0) を固定入力にして Phase 25.1b 側の selfhost builder / HakoCli.run MIR を Rust ラインと diff する、という順番で進める。
|
||||
|
||||
@ -17,9 +17,28 @@ using lang.compiler.entry.func_scanner as FuncScannerBox
|
||||
using lang.compiler.entry.using_resolver as Stage1UsingResolverBox
|
||||
using lang.compiler.builder.mod as CompilerBuilder
|
||||
|
||||
// Dev-only trace helper (Phase 25.1c)
|
||||
// - Enabled when HAKO_STAGEB_TRACE=1
|
||||
// - Keeps Stage-B behavior unchanged(ログのみ追加)
|
||||
static box StageBTraceBox {
|
||||
log(label) {
|
||||
local flag = env.get("HAKO_STAGEB_TRACE")
|
||||
if flag == null { return 0 }
|
||||
if ("" + flag) != "1" { return 0 }
|
||||
// label が null/Void でも落ちないように守る(dev専用)
|
||||
local msg = "[stageb/trace]"
|
||||
if label != null {
|
||||
msg = msg + " " + ("" + label)
|
||||
}
|
||||
print(msg)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 25.1c: CLI argument → source resolution
|
||||
static box StageBArgsBox {
|
||||
resolve_src(args) {
|
||||
StageBTraceBox.log("StageBArgsBox.resolve_src:enter")
|
||||
// 1) Collect source from args or env
|
||||
local src = null
|
||||
local src_file = null
|
||||
@ -43,6 +62,13 @@ static box StageBArgsBox {
|
||||
// Original: if src == null { src = env.local.get("HAKO_SOURCE") }
|
||||
if src == null { src = "return 0" }
|
||||
|
||||
{
|
||||
// Trace final source length(dev専用)
|
||||
local l = 0
|
||||
if src != null { l = ("" + src).length() }
|
||||
StageBTraceBox.log("StageBArgsBox.resolve_src:return_len=" + ("" + l))
|
||||
}
|
||||
|
||||
return src
|
||||
}
|
||||
}
|
||||
@ -50,6 +76,12 @@ static box StageBArgsBox {
|
||||
// Phase 25.1c: Body extraction + bundle + using + trim
|
||||
static box StageBBodyExtractorBox {
|
||||
build_body_src(src, args) {
|
||||
{
|
||||
//入口トレース: 入力ソース長と引数有無
|
||||
local l = 0
|
||||
if src != null { l = ("" + src).length() }
|
||||
StageBTraceBox.log("StageBBodyExtractorBox.build_body_src:enter len=" + ("" + l))
|
||||
}
|
||||
// ============================================================================
|
||||
// Depth guard: prevent accidental recursion inside Stage‑B body extractor
|
||||
// ============================================================================
|
||||
@ -609,6 +641,13 @@ static box StageBBodyExtractorBox {
|
||||
if e > b { body_src = s.substring(b, e) } else { body_src = "" }
|
||||
}
|
||||
|
||||
{
|
||||
//出口トレース: 抽出後 body_src 長
|
||||
local l2 = 0
|
||||
if body_src != null { l2 = ("" + body_src).length() }
|
||||
StageBTraceBox.log("StageBBodyExtractorBox.build_body_src:return_len=" + ("" + l2))
|
||||
}
|
||||
|
||||
// Clear depth guard before returning
|
||||
env.set("HAKO_STAGEB_BODY_DEPTH", "0")
|
||||
return body_src
|
||||
@ -630,7 +669,14 @@ static box StageBDriverBox {
|
||||
env.set("HAKO_STAGEB_DRIVER_DEPTH", "1")
|
||||
}
|
||||
|
||||
StageBTraceBox.log("StageBDriverBox.main:enter")
|
||||
|
||||
local src = StageBArgsBox.resolve_src(args)
|
||||
{
|
||||
local l = 0
|
||||
if src != null { l = ("" + src).length() }
|
||||
StageBTraceBox.log("StageBDriverBox.main:after_resolve_src len=" + ("" + l))
|
||||
}
|
||||
|
||||
// 2) Stage‑3 acceptance default ON for selfhost (env may turn off; keep tolerant here)
|
||||
local p = new ParserBox()
|
||||
@ -643,11 +689,22 @@ static box StageBDriverBox {
|
||||
// local externs_json = p.get_externs_json()
|
||||
|
||||
local body_src = StageBBodyExtractorBox.build_body_src(src, args)
|
||||
{
|
||||
local l2 = 0
|
||||
if body_src != null { l2 = ("" + body_src).length() }
|
||||
StageBTraceBox.log("StageBDriverBox.main:after_build_body_src len=" + ("" + l2))
|
||||
}
|
||||
|
||||
// 6) Parse and emit Stage‑1 JSON v0 (Program)
|
||||
// Bridge(JSON v0) が Program v0 を受け取り MIR に lowering するため、ここでは AST(JSON v0) を出力する。
|
||||
// 既定で MIR 直出力は行わない(重い経路を避け、一行出力を保証)。
|
||||
local ast_json = p.parse_program2(body_src)
|
||||
{
|
||||
// AST(JSON v0) の長さを軽く観測
|
||||
local la = 0
|
||||
if ast_json != null { la = ("" + ast_json).length() }
|
||||
StageBTraceBox.log("StageBDriverBox.main:after_parse_program2 len=" + ("" + la))
|
||||
}
|
||||
|
||||
// 6.3) Apply SSA transformations (CompilerBuilder pipeline)
|
||||
{
|
||||
@ -686,6 +743,12 @@ static box StageBDriverBox {
|
||||
// Use FuncScannerBox to extract method definitions from all boxes
|
||||
local methods = FuncScannerBox.scan_all_boxes(src)
|
||||
|
||||
{
|
||||
local cnt = 0
|
||||
if methods != null { cnt = methods.length() }
|
||||
StageBTraceBox.log("StageBDriverBox.main:func_scan methods=" + ("" + cnt))
|
||||
}
|
||||
|
||||
// Build defs JSON array
|
||||
if methods.length() > 0 {
|
||||
defs_json = ",\"defs\":["
|
||||
@ -734,6 +797,7 @@ static box StageBDriverBox {
|
||||
}
|
||||
|
||||
print(ast_json)
|
||||
StageBTraceBox.log("StageBDriverBox.main:exit rc=0")
|
||||
// Clear depth guard before returning
|
||||
env.set("HAKO_STAGEB_DRIVER_DEPTH", "0")
|
||||
return 0
|
||||
|
||||
@ -176,6 +176,8 @@ static box ParserControlBox {
|
||||
if j < src.length() { j = j + 1 } else { j = src.length() }
|
||||
}
|
||||
ctx.gpos_set(j)
|
||||
// Reset recursion guard before returning (Stage-3 path)
|
||||
env.set("HAKO_STAGEB_BREAK_DEPTH", "0")
|
||||
return "{\"type\":\"Break\"}"
|
||||
}
|
||||
|
||||
@ -205,6 +207,8 @@ static box ParserControlBox {
|
||||
if j < src.length() { j = j + 1 } else { j = src.length() }
|
||||
}
|
||||
ctx.gpos_set(j)
|
||||
// Reset recursion guard before returning (Stage-3 path)
|
||||
env.set("HAKO_STAGEB_CONTINUE_DEPTH", "0")
|
||||
return "{\"type\":\"Continue\"}"
|
||||
}
|
||||
|
||||
|
||||
@ -53,12 +53,17 @@ static box ParserStmtBox {
|
||||
ctx.gpos_set(j)
|
||||
// Record annotation in parser context and emit no statement
|
||||
ctx.add_extern_c(sym, func_name)
|
||||
// Reset recursion guard before returning(1回の parse 呼び出しごとに depth をクリアする)
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return ""
|
||||
}
|
||||
|
||||
// using statement
|
||||
if ctx.starts_with_kw(src, j, "using") == 1 {
|
||||
return me.parse_using(src, j, stmt_start, ctx)
|
||||
local out_using = me.parse_using(src, j, stmt_start, ctx)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return out_using
|
||||
}
|
||||
|
||||
// assignment: IDENT '=' expr
|
||||
@ -90,6 +95,8 @@ static box ParserStmtBox {
|
||||
if k0 < src.length() { k0 = k0 + 1 } else { k0 = src.length() }
|
||||
}
|
||||
ctx.gpos_set(k0)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return "{\"type\":\"Local\",\"name\":\"" + name0 + "\",\"expr\":" + expr_json0 + "}"
|
||||
}
|
||||
}
|
||||
@ -115,6 +122,8 @@ static box ParserStmtBox {
|
||||
if j < src.length() { j = j + 1 } else { j = src.length() }
|
||||
}
|
||||
ctx.gpos_set(j)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return "{\"type\":\"Return\",\"expr\":" + expr_json_ret + "}"
|
||||
}
|
||||
|
||||
@ -157,32 +166,52 @@ static box ParserStmtBox {
|
||||
if j < src.length() { j = j + 1 } else { j = src.length() }
|
||||
}
|
||||
ctx.gpos_set(j)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return "{\"type\":\"Local\",\"name\":\"" + name + "\",\"expr\":" + expr_json_local + "}"
|
||||
}
|
||||
|
||||
// Delegate to specialized boxes
|
||||
if ctx.starts_with_kw(src, j, "if") == 1 {
|
||||
return ParserControlBox.parse_if(src, j, stmt_start, ctx)
|
||||
local out_if = ParserControlBox.parse_if(src, j, stmt_start, ctx)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return out_if
|
||||
}
|
||||
|
||||
if ctx.starts_with_kw(src, j, "loop") == 1 {
|
||||
return ParserControlBox.parse_loop(src, j, stmt_start, ctx)
|
||||
local out_loop = ParserControlBox.parse_loop(src, j, stmt_start, ctx)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return out_loop
|
||||
}
|
||||
|
||||
if ctx.starts_with_kw(src, j, "break") == 1 {
|
||||
return ParserControlBox.parse_break(src, j, stmt_start, ctx)
|
||||
local out_break = ParserControlBox.parse_break(src, j, stmt_start, ctx)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return out_break
|
||||
}
|
||||
|
||||
if ctx.starts_with_kw(src, j, "continue") == 1 {
|
||||
return ParserControlBox.parse_continue(src, j, stmt_start, ctx)
|
||||
local out_cont = ParserControlBox.parse_continue(src, j, stmt_start, ctx)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return out_cont
|
||||
}
|
||||
|
||||
if ctx.starts_with_kw(src, j, "throw") == 1 {
|
||||
return ParserExceptionBox.parse_throw(src, j, stmt_start, ctx)
|
||||
local out_throw = ParserExceptionBox.parse_throw(src, j, stmt_start, ctx)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return out_throw
|
||||
}
|
||||
|
||||
if ctx.starts_with_kw(src, j, "try") == 1 {
|
||||
return ParserExceptionBox.parse_try(src, j, stmt_start, ctx)
|
||||
local out_try = ParserExceptionBox.parse_try(src, j, stmt_start, ctx)
|
||||
// Reset recursion guard before returning
|
||||
env.set("HAKO_STAGEB_STMT_DEPTH", "0")
|
||||
return out_try
|
||||
}
|
||||
|
||||
// Fallback: expression or unknown token
|
||||
|
||||
Reference in New Issue
Block a user