feat(parsercontrol): add shallow recursion guards to ParserControlBox (if/loop/break/continue/block)
This commit is contained in:
@ -7,6 +7,16 @@ using sh_core as StringHelpers // Required: using chain resolution not implemen
|
|||||||
static box ParserControlBox {
|
static box ParserControlBox {
|
||||||
// Parse: if (cond) { ... } else { ... }
|
// Parse: if (cond) { ... } else { ... }
|
||||||
parse_if(src, i, stmt_start, ctx) {
|
parse_if(src, i, stmt_start, ctx) {
|
||||||
|
// Shallow recursion guard for Stage‑B / selfhost: protect parse_if from
|
||||||
|
// accidental self-recursion via malformed input or control flow bugs.
|
||||||
|
{
|
||||||
|
local depth = env.get("HAKO_STAGEB_IF_DEPTH")
|
||||||
|
if depth != null && ("" + depth) != "0" {
|
||||||
|
print("[stageb/recursion] ParserControlBox.parse_if recursion detected")
|
||||||
|
return "{\"type\":\"If\",\"cond\":{\"type\":\"Bool\",\"value\":false},\"then\":[]}"
|
||||||
|
}
|
||||||
|
env.set("HAKO_STAGEB_IF_DEPTH", "1")
|
||||||
|
}
|
||||||
local j = i + 2 // skip "if"
|
local j = i + 2 // skip "if"
|
||||||
j = ctx.skip_ws(src, j)
|
j = ctx.skip_ws(src, j)
|
||||||
local paren = 0
|
local paren = 0
|
||||||
@ -42,6 +52,7 @@ static box ParserControlBox {
|
|||||||
}
|
}
|
||||||
ctx.gpos_set(j)
|
ctx.gpos_set(j)
|
||||||
|
|
||||||
|
env.set("HAKO_STAGEB_IF_DEPTH", "0")
|
||||||
if else_json == null {
|
if else_json == null {
|
||||||
return "{\"type\":\"If\",\"cond\":" + cond + ",\"then\":" + then_json + "}"
|
return "{\"type\":\"If\",\"cond\":" + cond + ",\"then\":" + then_json + "}"
|
||||||
} else {
|
} else {
|
||||||
@ -52,6 +63,16 @@ static box ParserControlBox {
|
|||||||
// Parse: loop(cond) { ... }
|
// Parse: loop(cond) { ... }
|
||||||
// Dev tracing: set HAKO_PARSER_TRACE_LOOP=1 to enable debug prints
|
// Dev tracing: set HAKO_PARSER_TRACE_LOOP=1 to enable debug prints
|
||||||
parse_loop(src, i, stmt_start, ctx) {
|
parse_loop(src, i, stmt_start, ctx) {
|
||||||
|
// Shallow recursion guard for parse_loop
|
||||||
|
{
|
||||||
|
local depth = env.get("HAKO_STAGEB_LOOP_DEPTH")
|
||||||
|
if depth != null && ("" + depth) != "0" {
|
||||||
|
print("[stageb/recursion] ParserControlBox.parse_loop recursion detected")
|
||||||
|
return "{\"type\":\"Loop\",\"cond\":{\"type\":\"Bool\",\"value\":false},\"body\":[]}"
|
||||||
|
}
|
||||||
|
env.set("HAKO_STAGEB_LOOP_DEPTH", "1")
|
||||||
|
}
|
||||||
|
|
||||||
local trace = 0 // dev-only (disabled in Stage-B runtime: no env API here)
|
local trace = 0 // dev-only (disabled in Stage-B runtime: no env API here)
|
||||||
local j = i + 4 // skip "loop"
|
local j = i + 4 // skip "loop"
|
||||||
j = ctx.skip_ws(src, j)
|
j = ctx.skip_ws(src, j)
|
||||||
@ -133,11 +154,20 @@ static box ParserControlBox {
|
|||||||
if j < src.length() { j = j + 1 } else { j = src.length() }
|
if j < src.length() { j = j + 1 } else { j = src.length() }
|
||||||
}
|
}
|
||||||
ctx.gpos_set(j)
|
ctx.gpos_set(j)
|
||||||
|
env.set("HAKO_STAGEB_LOOP_DEPTH", "0")
|
||||||
return "{\"type\":\"Loop\",\"cond\":" + cond + ",\"body\":" + body_json + "}"
|
return "{\"type\":\"Loop\",\"cond\":" + cond + ",\"body\":" + body_json + "}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse: break → {type:"Break"} (Stage-3) or no-op
|
// Parse: break → {type:"Break"} (Stage-3) or no-op
|
||||||
parse_break(src, i, stmt_start, ctx) {
|
parse_break(src, i, stmt_start, ctx) {
|
||||||
|
{
|
||||||
|
local depth = env.get("HAKO_STAGEB_BREAK_DEPTH")
|
||||||
|
if depth != null && ("" + depth) != "0" {
|
||||||
|
print("[stageb/recursion] ParserControlBox.parse_break recursion detected")
|
||||||
|
return "{\"type\":\"Break\"}"
|
||||||
|
}
|
||||||
|
env.set("HAKO_STAGEB_BREAK_DEPTH", "1")
|
||||||
|
}
|
||||||
local j = i + 5 // skip "break"
|
local j = i + 5 // skip "break"
|
||||||
|
|
||||||
if ctx.stage3_enabled() == 1 {
|
if ctx.stage3_enabled() == 1 {
|
||||||
@ -153,11 +183,20 @@ static box ParserControlBox {
|
|||||||
if j < src.length() { j = j + 1 } else { j = src.length() }
|
if j < src.length() { j = j + 1 } else { j = src.length() }
|
||||||
}
|
}
|
||||||
ctx.gpos_set(j)
|
ctx.gpos_set(j)
|
||||||
|
env.set("HAKO_STAGEB_BREAK_DEPTH", "0")
|
||||||
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
|
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse: continue → {type:"Continue"} (Stage-3) or no-op
|
// Parse: continue → {type:"Continue"} (Stage-3) or no-op
|
||||||
parse_continue(src, i, stmt_start, ctx) {
|
parse_continue(src, i, stmt_start, ctx) {
|
||||||
|
{
|
||||||
|
local depth = env.get("HAKO_STAGEB_CONTINUE_DEPTH")
|
||||||
|
if depth != null && ("" + depth) != "0" {
|
||||||
|
print("[stageb/recursion] ParserControlBox.parse_continue recursion detected")
|
||||||
|
return "{\"type\":\"Continue\"}"
|
||||||
|
}
|
||||||
|
env.set("HAKO_STAGEB_CONTINUE_DEPTH", "1")
|
||||||
|
}
|
||||||
local j = i + 8 // skip "continue"
|
local j = i + 8 // skip "continue"
|
||||||
|
|
||||||
if ctx.stage3_enabled() == 1 {
|
if ctx.stage3_enabled() == 1 {
|
||||||
@ -173,11 +212,20 @@ static box ParserControlBox {
|
|||||||
if j < src.length() { j = j + 1 } else { j = src.length() }
|
if j < src.length() { j = j + 1 } else { j = src.length() }
|
||||||
}
|
}
|
||||||
ctx.gpos_set(j)
|
ctx.gpos_set(j)
|
||||||
|
env.set("HAKO_STAGEB_CONTINUE_DEPTH", "0")
|
||||||
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
|
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse: { stmt1; stmt2; ... }
|
// Parse: { stmt1; stmt2; ... }
|
||||||
parse_block(src, i, ctx) {
|
parse_block(src, i, ctx) {
|
||||||
|
{
|
||||||
|
local depth = env.get("HAKO_STAGEB_BLOCK_DEPTH")
|
||||||
|
if depth != null && ("" + depth) != "0" {
|
||||||
|
print("[stageb/recursion] ParserControlBox.parse_block recursion detected")
|
||||||
|
return "[]@" + ctx.i2s(i)
|
||||||
|
}
|
||||||
|
env.set("HAKO_STAGEB_BLOCK_DEPTH", "1")
|
||||||
|
}
|
||||||
local j = ctx.skip_ws(src, i)
|
local j = ctx.skip_ws(src, i)
|
||||||
if src.substring(j, j+1) != "{" { return "[]@" + ctx.i2s(j) }
|
if src.substring(j, j+1) != "{" { return "[]@" + ctx.i2s(j) }
|
||||||
j = j + 1
|
j = j + 1
|
||||||
@ -231,6 +279,7 @@ static box ParserControlBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body = body + "]"
|
body = body + "]"
|
||||||
|
env.set("HAKO_STAGEB_BLOCK_DEPTH", "0")
|
||||||
return body + "@" + ctx.i2s(j)
|
return body + "@" + ctx.i2s(j)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user