feat(parsercontrol): add shallow recursion guards to ParserControlBox (if/loop/break/continue/block)

This commit is contained in:
nyash-codex
2025-11-17 18:23:00 +09:00
parent 978890e7f6
commit f3cd815c77

View File

@ -7,6 +7,16 @@ using sh_core as StringHelpers // Required: using chain resolution not implemen
static box ParserControlBox {
// Parse: if (cond) { ... } else { ... }
parse_if(src, i, stmt_start, ctx) {
// Shallow recursion guard for StageB / 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"
j = ctx.skip_ws(src, j)
local paren = 0
@ -42,6 +52,7 @@ static box ParserControlBox {
}
ctx.gpos_set(j)
env.set("HAKO_STAGEB_IF_DEPTH", "0")
if else_json == null {
return "{\"type\":\"If\",\"cond\":" + cond + ",\"then\":" + then_json + "}"
} else {
@ -52,6 +63,16 @@ static box ParserControlBox {
// Parse: loop(cond) { ... }
// Dev tracing: set HAKO_PARSER_TRACE_LOOP=1 to enable debug prints
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 j = i + 4 // skip "loop"
j = ctx.skip_ws(src, j)
@ -133,11 +154,20 @@ static box ParserControlBox {
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
env.set("HAKO_STAGEB_LOOP_DEPTH", "0")
return "{\"type\":\"Loop\",\"cond\":" + cond + ",\"body\":" + body_json + "}"
}
// Parse: break → {type:"Break"} (Stage-3) or no-op
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"
if ctx.stage3_enabled() == 1 {
@ -153,11 +183,20 @@ static box ParserControlBox {
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
env.set("HAKO_STAGEB_BREAK_DEPTH", "0")
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
}
// Parse: continue → {type:"Continue"} (Stage-3) or no-op
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"
if ctx.stage3_enabled() == 1 {
@ -173,11 +212,20 @@ static box ParserControlBox {
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
env.set("HAKO_STAGEB_CONTINUE_DEPTH", "0")
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
}
// Parse: { stmt1; stmt2; ... }
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)
if src.substring(j, j+1) != "{" { return "[]@" + ctx.i2s(j) }
j = j + 1
@ -231,6 +279,7 @@ static box ParserControlBox {
}
body = body + "]"
env.set("HAKO_STAGEB_BLOCK_DEPTH", "0")
return body + "@" + ctx.i2s(j)
}
}