From 54b9597af6f3bfc9b5dfb2bd8282b2463fc5b46b Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Mon, 24 Nov 2025 18:34:43 +0900 Subject: [PATCH] fix(parser): Stage-1 CLI infinite loop - fix ParserExprBox and ParserIdentScanBox (Part 2/3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Problem**: Multiple infinite loops in parser expression handling - ParserIdentScanBox.scan_ident: bb985 - ParserExprBox.parse_factor2: bb1662 (2 loops) - ParserExprBox.parse_term2: bb1704 - ParserExprBox.parse_sum2 - ParserExprBox.parse_expr2 - ParserExprBox.parse_args2 **Root Cause**: Same `loop(cont == 1)` + nested if-else pattern as Part 1 - MirBuilder generates PHI self-references - Loop exits jump to wrong blocks **Solution**: MirBuilder-friendly explicit continue/break pattern ```hako // BAD: loop(cont == 1) { if condition { action } else { cont = 0 } } // GOOD: loop(true) { if condition { action; continue } break } ``` **Files Modified**: - lang/src/compiler/parser/scan/parser_ident_scan_box.hako: scan_ident explicit continue - lang/src/compiler/parser/expr/parser_expr_box.hako: 6 loops fixed - parse_factor2: 2 method chaining loops (new/identifier) - parse_term2: */ operators - parse_sum2: +- operators - parse_expr2: &&|| operators - parse_args2: argument list parsing **Progress**: 9 total loop patterns fixed (Part 1: 3, Part 2: 6) **Remaining**: parse_program2 still has infinite loop at different location (bb2816 vs original bb2854) - investigating in Part 3 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../compiler/parser/expr/parser_expr_box.hako | 135 ++++++++---------- .../parser/scan/parser_ident_scan_box.hako | 8 +- 2 files changed, 66 insertions(+), 77 deletions(-) diff --git a/lang/src/compiler/parser/expr/parser_expr_box.hako b/lang/src/compiler/parser/expr/parser_expr_box.hako index 96312c97..8e5a67d8 100644 --- a/lang/src/compiler/parser/expr/parser_expr_box.hako +++ b/lang/src/compiler/parser/expr/parser_expr_box.hako @@ -128,9 +128,8 @@ static box ParserExprBox { if src.substring(k, k+1) == ")" { k = k + 1 } // Build initial New node local node = "{\"type\":\"New\",\"class\":\"" + cls + "\",\"args\":" + args_json + "}" - // Optional method chain: .method(args...) - local cont_new = 1 - loop(cont_new == 1) { + // Optional method chain: .method(args...) - MirBuilder-friendly + loop(true) { k = ctx.skip_ws(src, k) if k >= src.length() { break } local tch = src.substring(k, k+1) @@ -150,9 +149,9 @@ static box ParserExprBox { k = ctx.skip_ws(src, k) if src.substring(k, k+1) == ")" { k = k + 1 } node = "{\"type\":\"Method\",\"recv\":" + node + ",\"method\":\"" + mname + "\",\"args\":" + args_json2 + "}" - } else { - cont_new = 0 + continue } + break } ctx.gpos_set(k) return node @@ -165,13 +164,13 @@ static box ParserExprBox { local name = idp.substring(0, at) local k = ctx.to_int(idp.substring(at+1, idp.length())) local node = "{\"type\":\"Var\",\"name\":\"" + name + "\"}" - local cont2 = 1 - loop(cont2 == 1) { + // MirBuilder-friendly: explicit continue/break instead of flag + loop(true) { k = ctx.skip_ws(src, k) local tch = src.substring(k, k+1) - if tch == "(" { + if tch == "(" { k = k + 1 local args_and_pos = ParserExprBox.parse_args2(src, k, ctx) local at2 = args_and_pos.lastIndexOf("@") @@ -180,27 +179,29 @@ static box ParserExprBox { k = ctx.skip_ws(src, k) if src.substring(k, k+1) == ")" { k = k + 1 } node = "{\"type\":\"Call\",\"name\":\"" + name + "\",\"args\":" + args_json + "}" - } else { - if tch == "." { - k = k + 1 - k = ctx.skip_ws(src, k) - local midp = ctx.read_ident2(src, k) - local at3 = midp.lastIndexOf("@") - local mname = midp.substring(0, at3) - k = ctx.to_int(midp.substring(at3+1, midp.length())) - k = ctx.skip_ws(src, k) - if src.substring(k, k+1) == "(" { k = k + 1 } - local args2 = ParserExprBox.parse_args2(src, k, ctx) - local at4 = args2.lastIndexOf("@") - local args_json2 = args2.substring(0, at4) - k = ctx.to_int(args2.substring(at4+1, args2.length())) - k = ctx.skip_ws(src, k) - if src.substring(k, k+1) == ")" { k = k + 1 } - node = "{\"type\":\"Method\",\"recv\":" + node + ",\"method\":\"" + mname + "\",\"args\":" + args_json2 + "}" - } else { - cont2 = 0 - } + continue } + + if tch == "." { + k = k + 1 + k = ctx.skip_ws(src, k) + local midp = ctx.read_ident2(src, k) + local at3 = midp.lastIndexOf("@") + local mname = midp.substring(0, at3) + k = ctx.to_int(midp.substring(at3+1, midp.length())) + k = ctx.skip_ws(src, k) + if src.substring(k, k+1) == "(" { k = k + 1 } + local args2 = ParserExprBox.parse_args2(src, k, ctx) + local at4 = args2.lastIndexOf("@") + local args_json2 = args2.substring(0, at4) + k = ctx.to_int(args2.substring(at4+1, args2.length())) + k = ctx.skip_ws(src, k) + if src.substring(k, k+1) == ")" { k = k + 1 } + node = "{\"type\":\"Method\",\"recv\":" + node + ",\"method\":\"" + mname + "\",\"args\":" + args_json2 + "}" + continue + } + + break } ctx.gpos_set(k) @@ -226,22 +227,18 @@ static box ParserExprBox { parse_term2(src, i, ctx) { local lhs = ParserExprBox.parse_unary2(src, i, ctx) local j = ctx.gpos_get() - local cont = 1 - loop(cont == 1) { + // MirBuilder-friendly: explicit continue/break instead of flag + loop(true) { j = ctx.skip_ws(src, j) - if j >= src.length() { - cont = 0 - } else { - local op = src.substring(j, j+1) - if op != "*" && op != "/" { - cont = 0 - } else { - local rhs = ParserExprBox.parse_unary2(src, j+1, ctx) - j = ctx.gpos_get() - lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" - } - } + if j >= src.length() { break } + + local op = src.substring(j, j+1) + if op != "*" && op != "/" { break } + + local rhs = ParserExprBox.parse_unary2(src, j+1, ctx) + j = ctx.gpos_get() + lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" } ctx.gpos_set(j) @@ -251,22 +248,18 @@ static box ParserExprBox { parse_sum2(src, i, ctx) { local lhs = ParserExprBox.parse_term2(src, i, ctx) local j = ctx.gpos_get() - local cont = 1 - loop(cont == 1) { + // MirBuilder-friendly: explicit continue/break instead of flag + loop(true) { j = ctx.skip_ws(src, j) - if j >= src.length() { - cont = 0 - } else { - local op = src.substring(j, j+1) - if op != "+" && op != "-" { - cont = 0 - } else { - local rhs = ParserExprBox.parse_term2(src, j+1, ctx) - j = ctx.gpos_get() - lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" - } - } + if j >= src.length() { break } + + local op = src.substring(j, j+1) + if op != "+" && op != "-" { break } + + local rhs = ParserExprBox.parse_term2(src, j+1, ctx) + j = ctx.gpos_get() + lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" } ctx.gpos_set(j) @@ -305,18 +298,16 @@ static box ParserExprBox { parse_expr2(src, i, ctx) { local lhs = ParserExprBox.parse_compare2(src, i, ctx) local j = ctx.gpos_get() - local cont = 1 - loop(cont == 1) { + // MirBuilder-friendly: explicit continue/break instead of flag + loop(true) { j = ctx.skip_ws(src, j) local two = src.substring(j, j+2) - if two != "&&" && two != "||" { - cont = 0 - } else { - local rhs = ParserExprBox.parse_compare2(src, j+2, ctx) - j = ctx.gpos_get() - lhs = "{\"type\":\"Logical\",\"op\":\"" + two + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" - } + if two != "&&" && two != "||" { break } + + local rhs = ParserExprBox.parse_compare2(src, j+2, ctx) + j = ctx.gpos_get() + lhs = "{\"type\":\"Logical\",\"op\":\"" + two + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" } j = ctx.skip_ws(src, j) @@ -354,13 +345,8 @@ static box ParserExprBox { j = ctx.gpos_get() out = out + e - // subsequent arguments with guard - local cont_args = 1 - local guard = 0 - local max = 100000 - - loop(cont_args == 1) { - if guard > max { cont_args = 0 } else { guard = guard + 1 } + // subsequent arguments - MirBuilder-friendly + loop(true) { local before = j j = ctx.skip_ws(src, j) @@ -370,11 +356,10 @@ static box ParserExprBox { e = ParserExprBox.parse_expr2(src, j, ctx) j = ctx.gpos_get() out = out + "," + e - } else { - cont_args = 0 + continue } - if j == before { cont_args = 0 } + break } out = out + "]" diff --git a/lang/src/compiler/parser/scan/parser_ident_scan_box.hako b/lang/src/compiler/parser/scan/parser_ident_scan_box.hako index c2107932..41a64092 100644 --- a/lang/src/compiler/parser/scan/parser_ident_scan_box.hako +++ b/lang/src/compiler/parser/scan/parser_ident_scan_box.hako @@ -10,10 +10,14 @@ static box ParserIdentScanBox { local ch = src.substring(j, j+1) if !(ParserStringUtilsBox.is_alpha(ch) || ch == "_") { return "@" + ParserStringUtilsBox.i2s(i) } j = j + 1 + // MirBuilder-friendly: explicit continue instead of if-else loop(j < n) { local ch2 = src.substring(j, j+1) - if ParserStringUtilsBox.is_alpha(ch2) || ParserStringUtilsBox.is_digit(ch2) || ch2 == "_" { j = j + 1 } - else { break } + if ParserStringUtilsBox.is_alpha(ch2) || ParserStringUtilsBox.is_digit(ch2) || ch2 == "_" { + j = j + 1 + continue + } + break } local s = src.substring(i, j) return s + "@" + ParserStringUtilsBox.i2s(j)