fix(parser): Stage-1 CLI infinite loop - fix ParserExprBox and ParserIdentScanBox (Part 2/3)

**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 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-24 18:34:43 +09:00
parent c6c5653ae0
commit 54b9597af6
2 changed files with 66 additions and 77 deletions

View File

@ -128,9 +128,8 @@ static box ParserExprBox {
if src.substring(k, k+1) == ")" { k = k + 1 } if src.substring(k, k+1) == ")" { k = k + 1 }
// Build initial New node // Build initial New node
local node = "{\"type\":\"New\",\"class\":\"" + cls + "\",\"args\":" + args_json + "}" local node = "{\"type\":\"New\",\"class\":\"" + cls + "\",\"args\":" + args_json + "}"
// Optional method chain: .method(args...) // Optional method chain: .method(args...) - MirBuilder-friendly
local cont_new = 1 loop(true) {
loop(cont_new == 1) {
k = ctx.skip_ws(src, k) k = ctx.skip_ws(src, k)
if k >= src.length() { break } if k >= src.length() { break }
local tch = src.substring(k, k+1) local tch = src.substring(k, k+1)
@ -150,9 +149,9 @@ static box ParserExprBox {
k = ctx.skip_ws(src, k) k = ctx.skip_ws(src, k)
if src.substring(k, k+1) == ")" { k = k + 1 } if src.substring(k, k+1) == ")" { k = k + 1 }
node = "{\"type\":\"Method\",\"recv\":" + node + ",\"method\":\"" + mname + "\",\"args\":" + args_json2 + "}" node = "{\"type\":\"Method\",\"recv\":" + node + ",\"method\":\"" + mname + "\",\"args\":" + args_json2 + "}"
} else { continue
cont_new = 0
} }
break
} }
ctx.gpos_set(k) ctx.gpos_set(k)
return node return node
@ -165,13 +164,13 @@ static box ParserExprBox {
local name = idp.substring(0, at) local name = idp.substring(0, at)
local k = ctx.to_int(idp.substring(at+1, idp.length())) local k = ctx.to_int(idp.substring(at+1, idp.length()))
local node = "{\"type\":\"Var\",\"name\":\"" + name + "\"}" 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) k = ctx.skip_ws(src, k)
local tch = src.substring(k, k+1) local tch = src.substring(k, k+1)
if tch == "(" { if tch == "(" {
k = k + 1 k = k + 1
local args_and_pos = ParserExprBox.parse_args2(src, k, ctx) local args_and_pos = ParserExprBox.parse_args2(src, k, ctx)
local at2 = args_and_pos.lastIndexOf("@") local at2 = args_and_pos.lastIndexOf("@")
@ -180,27 +179,29 @@ static box ParserExprBox {
k = ctx.skip_ws(src, k) k = ctx.skip_ws(src, k)
if src.substring(k, k+1) == ")" { k = k + 1 } if src.substring(k, k+1) == ")" { k = k + 1 }
node = "{\"type\":\"Call\",\"name\":\"" + name + "\",\"args\":" + args_json + "}" node = "{\"type\":\"Call\",\"name\":\"" + name + "\",\"args\":" + args_json + "}"
} else { 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 + "}"
} else {
cont2 = 0
}
} }
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) ctx.gpos_set(k)
@ -226,22 +227,18 @@ static box ParserExprBox {
parse_term2(src, i, ctx) { parse_term2(src, i, ctx) {
local lhs = ParserExprBox.parse_unary2(src, i, ctx) local lhs = ParserExprBox.parse_unary2(src, i, ctx)
local j = ctx.gpos_get() 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) j = ctx.skip_ws(src, j)
if j >= src.length() { if j >= src.length() { break }
cont = 0
} else { local op = src.substring(j, j+1)
local op = src.substring(j, j+1) if op != "*" && op != "/" { break }
if op != "*" && op != "/" {
cont = 0 local rhs = ParserExprBox.parse_unary2(src, j+1, ctx)
} else { j = ctx.gpos_get()
local rhs = ParserExprBox.parse_unary2(src, j+1, ctx) lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
j = ctx.gpos_get()
lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
}
}
} }
ctx.gpos_set(j) ctx.gpos_set(j)
@ -251,22 +248,18 @@ static box ParserExprBox {
parse_sum2(src, i, ctx) { parse_sum2(src, i, ctx) {
local lhs = ParserExprBox.parse_term2(src, i, ctx) local lhs = ParserExprBox.parse_term2(src, i, ctx)
local j = ctx.gpos_get() 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) j = ctx.skip_ws(src, j)
if j >= src.length() { if j >= src.length() { break }
cont = 0
} else { local op = src.substring(j, j+1)
local op = src.substring(j, j+1) if op != "+" && op != "-" { break }
if op != "+" && op != "-" {
cont = 0 local rhs = ParserExprBox.parse_term2(src, j+1, ctx)
} else { j = ctx.gpos_get()
local rhs = ParserExprBox.parse_term2(src, j+1, ctx) lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
j = ctx.gpos_get()
lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
}
}
} }
ctx.gpos_set(j) ctx.gpos_set(j)
@ -305,18 +298,16 @@ static box ParserExprBox {
parse_expr2(src, i, ctx) { parse_expr2(src, i, ctx) {
local lhs = ParserExprBox.parse_compare2(src, i, ctx) local lhs = ParserExprBox.parse_compare2(src, i, ctx)
local j = ctx.gpos_get() 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) j = ctx.skip_ws(src, j)
local two = src.substring(j, j+2) local two = src.substring(j, j+2)
if two != "&&" && two != "||" { if two != "&&" && two != "||" { break }
cont = 0
} else { local rhs = ParserExprBox.parse_compare2(src, j+2, ctx)
local rhs = ParserExprBox.parse_compare2(src, j+2, ctx) j = ctx.gpos_get()
j = ctx.gpos_get() lhs = "{\"type\":\"Logical\",\"op\":\"" + two + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
lhs = "{\"type\":\"Logical\",\"op\":\"" + two + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
}
} }
j = ctx.skip_ws(src, j) j = ctx.skip_ws(src, j)
@ -354,13 +345,8 @@ static box ParserExprBox {
j = ctx.gpos_get() j = ctx.gpos_get()
out = out + e out = out + e
// subsequent arguments with guard // subsequent arguments - MirBuilder-friendly
local cont_args = 1 loop(true) {
local guard = 0
local max = 100000
loop(cont_args == 1) {
if guard > max { cont_args = 0 } else { guard = guard + 1 }
local before = j local before = j
j = ctx.skip_ws(src, j) j = ctx.skip_ws(src, j)
@ -370,11 +356,10 @@ static box ParserExprBox {
e = ParserExprBox.parse_expr2(src, j, ctx) e = ParserExprBox.parse_expr2(src, j, ctx)
j = ctx.gpos_get() j = ctx.gpos_get()
out = out + "," + e out = out + "," + e
} else { continue
cont_args = 0
} }
if j == before { cont_args = 0 } break
} }
out = out + "]" out = out + "]"

View File

@ -10,10 +10,14 @@ static box ParserIdentScanBox {
local ch = src.substring(j, j+1) local ch = src.substring(j, j+1)
if !(ParserStringUtilsBox.is_alpha(ch) || ch == "_") { return "@" + ParserStringUtilsBox.i2s(i) } if !(ParserStringUtilsBox.is_alpha(ch) || ch == "_") { return "@" + ParserStringUtilsBox.i2s(i) }
j = j + 1 j = j + 1
// MirBuilder-friendly: explicit continue instead of if-else
loop(j < n) { loop(j < n) {
local ch2 = src.substring(j, j+1) local ch2 = src.substring(j, j+1)
if ParserStringUtilsBox.is_alpha(ch2) || ParserStringUtilsBox.is_digit(ch2) || ch2 == "_" { j = j + 1 } if ParserStringUtilsBox.is_alpha(ch2) || ParserStringUtilsBox.is_digit(ch2) || ch2 == "_" {
else { break } j = j + 1
continue
}
break
} }
local s = src.substring(i, j) local s = src.substring(i, j)
return s + "@" + ParserStringUtilsBox.i2s(j) return s + "@" + ParserStringUtilsBox.i2s(j)