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:
@ -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 + "]"
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user