hv1: early-exit at main (no plugin init); tokenizer: Stage-3 single-quote + full escapes (\/ \b \f \' \r fix); builder: route BinOp via SSOT emit_binop_to_dst; hv1 verify canary route (builder→Core); docs: phase-20.39 updates

This commit is contained in:
nyash-codex
2025-11-04 20:46:43 +09:00
parent 31ce798341
commit 44a5158a14
53 changed files with 2237 additions and 179 deletions

View File

@ -86,6 +86,8 @@ static box MirBuilderBox {
using "hako.mir.builder.internal.lower_return_binop_varvar" as LowerReturnBinOpVarVarBox
using "hako.mir.builder.internal.lower_return_binop" as LowerReturnBinOpBox
using "hako.mir.builder.internal.lower_return_int" as LowerReturnIntBox
// Prefer loop lowers first to catch loop-specific patterns (sum_bc/continue/break normalization)
{ local out_loop2 = LowerLoopSumBcBox.try_lower(s); if out_loop2 != null { return out_loop2 } }
{ local out_if2b = LowerIfNestedBox.try_lower(s); if out_if2b != null { return out_if2b } }
{ local out_if2 = LowerIfThenElseFollowingReturnBox.try_lower(s); if out_if2 != null { return out_if2 } }
{ local out_if = LowerIfCompareBox.try_lower(s); if out_if != null { return out_if } }
@ -93,7 +95,6 @@ static box MirBuilderBox {
{ local out_ifbv = LowerIfCompareFoldVarIntBox.try_lower(s); if out_ifbv != null { return out_ifbv } }
{ local out_ifvi = LowerIfCompareVarIntBox.try_lower(s); if out_ifvi != null { return out_ifvi } }
{ local out_ifvv = LowerIfCompareVarVarBox.try_lower(s); if out_ifvv != null { return out_ifvv } }
{ local out_loop2 = LowerLoopSumBcBox.try_lower(s); if out_loop2 != null { return out_loop2 } }
{ local out_loopp = LowerLoopCountParamBox.try_lower(s); if out_loopp != null { return out_loopp } }
{ local out_loop = LowerLoopSimpleBox.try_lower(s); if out_loop != null { return out_loop } }
{ local out_var = LowerReturnVarLocalBox.try_lower(s); if out_var != null { return out_var } }

View File

@ -0,0 +1,83 @@
// loop_scan_box.hako — If/Compare + then/else 範囲スキャンの小箱
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
using ProgScanBox as Scan
using selfhost.shared.json.utils.json_frag as JsonFragBox
static box LoopScanBox {
// 抽出: cond Compare から Var 名lhs/rhs いずれか)を取得
find_loop_var_name(s, k_cmp) {
local varname = null
local kl = ("" + s).indexOf("\"lhs\":{", k_cmp)
local kr = ("" + s).indexOf("\"rhs\":{", k_cmp)
if kl >= 0 && ("" + s).indexOf("\"type\":\"Var\"", kl) >= 0 { varname = Scan.read_quoted_after_key(s, kl, "name") }
if varname == null && kr >= 0 && ("" + s).indexOf("\"type\":\"Var\"", kr) >= 0 { varname = Scan.read_quoted_after_key(s, kr, "name") }
return varname
}
// '!=' + else [Break/Continue] パターンからX値を抽出最小サブセット
// sentinel: "Break" | "Continue"
extract_ne_else_sentinel_value(s, sentinel, k_loop, varname) {
local st = "\"type\":\"" + sentinel + "\""
local ks = ("" + s).indexOf(st, k_loop)
if ks < 0 { return null }
// 直前の If と Compare を見つける(同一 then/else 内の近傍に限定)
local kif = ("" + s).lastIndexOf("\"type\":\"If\"", ks)
if kif < 0 { return null }
local kcmp = ("" + s).lastIndexOf("\"type\":\"Compare\"", ks)
if kcmp < 0 || kcmp < kif { return null }
local op = Scan.read_quoted_after_key(s, kcmp, "op"); if op == null || op != "!=" { return null }
// else 範囲の配列区間を特定
local kth = JsonFragBox.index_of_from(s, "\"then\":", kif); if kth < 0 { return null }
local lb_then = JsonFragBox.index_of_from(s, "[", kth); if lb_then < 0 { return null }
local rb_then = JsonFragBox._seek_array_end(s, lb_then); if rb_then < 0 { return null }
local kel = JsonFragBox.index_of_from(s, "\"else\":", rb_then); if kel < 0 { return null }
local lb_else = JsonFragBox.index_of_from(s, "[", kel); if lb_else < 0 { return null }
local rb_else = JsonFragBox._seek_array_end(s, lb_else); if rb_else < 0 { return null }
// sentinel が else ブロック中にあること
if !(ks > lb_else && ks < rb_else) { return null }
// 比較の反対側 Int を抽出lhs=Var(varname) → rhs Int、rhs=Var → lhs Int
local has_lhs = ("" + s).indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", kcmp) >= 0
local has_rhs = ("" + s).indexOf("\"rhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", kcmp) >= 0
if !has_lhs && !has_rhs { return null }
if has_lhs {
local kr = ("" + s).indexOf("\"rhs\":{", kcmp); if kr < 0 { return null }
local kt = ("" + s).indexOf("\"type\":\"Int\"", kr); if kt < 0 { return null }
local sentinel_val = Scan.read_value_int_after(s, kt)
// Safety check: must be valid numeric string
if sentinel_val == null { return null }
local sval_str = "" + sentinel_val
if sval_str.length() == 0 { return null }
if sval_str.length() > 10 { return null }
// Must be numeric (basic check)
local i = 0; local len = sval_str.length()
loop(i < len) {
local ch = sval_str.substring(i, i + 1)
if ch < "0" || ch > "9" {
if !(i == 0 && ch == "-") { return null }
}
i = i + 1
}
return sentinel_val
}
// rhs が変数
local kl = ("" + s).indexOf("\"lhs\":{", kcmp); if kl < 0 { return null }
local kt2 = ("" + s).indexOf("\"type\":\"Int\"", kl); if kt2 < 0 { return null }
local sentinel_val2 = Scan.read_value_int_after(s, kt2)
// Safety check for rhs case
if sentinel_val2 == null { return null }
local sval_str2 = "" + sentinel_val2
if sval_str2.length() == 0 { return null }
if sval_str2.length() > 10 { return null }
local i2 = 0; local len2 = sval_str2.length()
loop(i2 < len2) {
local ch2 = sval_str2.substring(i2, i2 + 1)
if ch2 < "0" || ch2 > "9" {
if !(i2 == 0 && ch2 == "-") { return null }
}
i2 = i2 + 1
}
return sentinel_val2
}
}

View File

@ -3,47 +3,137 @@
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
using ProgScanBox as Scan
using selfhost.shared.mir.loopform as LoopFormBox
using selfhost.shared.common.string_helpers as StringHelpers
using selfhost.shared.json.utils.json_frag as JsonFragBox
using selfhost.mir.builder.internal.pattern_util_box as PatternUtilBox
using "hako.mir.builder.internal.loop_scan" as LoopScanBox
static box LowerLoopCountParamBox {
try_lower(program_json) {
local s = "" + program_json
// Local i = Int init
local k_local_i = s.indexOf("\"type\":\"Local\"")
if k_local_i < 0 { return null }
if s.indexOf("\"name\":\"i\"", k_local_i) < 0 { return null }
local k_init = s.indexOf("\"type\":\"Int\"", k_local_i)
if k_init < 0 { return null }
local init = Scan.read_value_int_after(s, k_init)
if init == null { return null }
// Loop Compare i < Int limit
local k_loop = s.indexOf("\"type\":\"Loop\"", k_local_i)
// Discover loop variable name from Compare first
// We'll accept either lhs Var(name) or rhs Var(name)
local k_loop = s.indexOf("\"type\":\"Loop\"", 0)
if k_loop < 0 { return null }
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_loop)
if k_cmp < 0 { return null }
if s.indexOf("\"op\":\"<\"", k_cmp) < 0 { return null }
if s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", k_cmp) < 0 { return null }
local k_lim_t = s.indexOf("\"type\":\"Int\"", k_cmp)
if k_lim_t < 0 { return null }
local limit = Scan.read_value_int_after(s, k_lim_t)
if limit == null { return null }
local varname = LoopScanBox.find_loop_var_name(s, k_cmp)
if varname == null { return null }
// Local <varname> = (Int init | Var initName)
local k_local_i = s.indexOf("\"type\":\"Local\"")
if k_local_i < 0 { return null }
if s.indexOf("\"name\":\"" + varname + "\"", k_local_i) < 0 { return null }
local init = null
{
local k_init_int = s.indexOf("\"type\":\"Int\"", k_local_i)
local k_loop_next = s.indexOf("\"type\":\"Loop\"", k_local_i)
if k_init_int >= 0 && (k_loop_next < 0 || k_init_int < k_loop_next) {
init = Scan.read_value_int_after(s, k_init_int)
} else {
local k_init_var = s.indexOf("\"type\":\"Var\"", k_local_i)
if k_init_var >= 0 && (k_loop_next < 0 || k_init_var < k_loop_next) {
local vname = Scan.read_quoted_after_key(s, k_init_var, "name")
if vname != null { init = PatternUtilBox.find_local_int_before(s, vname, k_local_i) }
}
}
}
if init == null { return null }
// Loop Compare normalize: accept < / <= / > / >= with Var(varname) on either side
// op: accept '<'/'<=' with i on lhs; '>'/'>=' with i on lhs (descending); swapped '>'/'>=' with i on rhs (ascending)
local op = Scan.read_quoted_after_key(s, k_cmp, "op")
if op == null { return null }
local has_lhs_i = s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", k_cmp) >= 0
local has_rhs_i = s.indexOf("\"rhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", k_cmp) >= 0
if !has_lhs_i && !has_rhs_i { return null }
local cmp = null
local limit = null
if has_lhs_i {
if op == "<" || op == "<=" {
// i < L / i <= L
local k_rhs = s.indexOf("\"rhs\":{", k_cmp); if k_rhs < 0 { return null }
local k_lim_t = s.indexOf("\"type\":\"Int\"", k_rhs)
if k_lim_t >= 0 {
limit = Scan.read_value_int_after(s, k_lim_t)
} else {
// rhs Var → reverse-lookup Local Int
local k_rv = s.indexOf("\"type\":\"Var\"", k_rhs); if k_rv < 0 { return null }
local lname = Scan.read_quoted_after_key(s, k_rhs, "name"); if lname == null { return null }
limit = PatternUtilBox.find_local_int_before(s, lname, k_cmp)
}
if limit == null { return null }
if op == "<=" { limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1) }
cmp = "Lt"
} else if op == ">" || op == ">=" {
// i > L / i >= L (descending)
local k_rhs2 = s.indexOf("\"rhs\":{", k_cmp); if k_rhs2 < 0 { return null }
local k_lim_t3 = s.indexOf("\"type\":\"Int\"", k_rhs2)
if k_lim_t3 >= 0 {
limit = Scan.read_value_int_after(s, k_lim_t3)
} else {
local k_rv2 = s.indexOf("\"type\":\"Var\"", k_rhs2); if k_rv2 < 0 { return null }
local lname2 = Scan.read_quoted_after_key(s, k_rhs2, "name"); if lname2 == null { return null }
limit = PatternUtilBox.find_local_int_before(s, lname2, k_cmp)
}
if limit == null { return null }
cmp = (op == ">") ? "Gt" : "Ge"
} else { return null }
} else {
// swapped (Int on lhs, Var i on rhs): L > i / L >= i (ascending)
if op != ">" && op != ">=" { return null }
local k_lhs = s.indexOf("\"lhs\":{", k_cmp); if k_lhs < 0 { return null }
local k_lim_t2 = s.indexOf("\"type\":\"Int\"", k_lhs)
if k_lim_t2 >= 0 {
limit = Scan.read_value_int_after(s, k_lim_t2)
} else {
local k_lv = s.indexOf("\"type\":\"Var\"", k_lhs); if k_lv < 0 { return null }
local lname3 = Scan.read_quoted_after_key(s, k_lhs, "name"); if lname3 == null { return null }
limit = PatternUtilBox.find_local_int_before(s, lname3, k_cmp)
}
if limit == null { return null }
if op == ">=" { limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1) }
cmp = "Lt"
}
// Body increment: Local i = Binary('+', Var i, Int step)
local k_body_i = s.indexOf("\"name\":\"i\"", k_loop)
local k_body_i = s.indexOf("\"name\":\"" + varname + "\"", k_loop)
if k_body_i < 0 { return null }
local k_bop = s.indexOf("\"type\":\"Binary\"", k_body_i)
if k_bop < 0 { return null }
if s.indexOf("\"op\":\"+\"", k_bop) < 0 { return null }
if s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", k_bop) < 0 { return null }
local k_step_t = s.indexOf("\"type\":\"Int\"", k_bop)
if k_step_t < 0 { return null }
local step = Scan.read_value_int_after(s, k_step_t)
if step == null { return null }
// Body increment: Local i = Binary(op '+' or '-', Var i, (Int step | Var stepName))
local bop_plus = (s.indexOf("\"op\":\"+\"", k_bop) >= 0)
local bop_minus = (s.indexOf("\"op\":\"-\"", k_bop) >= 0)
if (!bop_plus && !bop_minus) { return null }
if s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", k_bop) < 0 { return null }
// Build via LoopFormBox.build2 ({ mode:"count", init, limit, step })
local step = null
// Prefer rhs Int if present; otherwise try rhs Var and reverse-lookup its Local Int
{
local k_rhsb = s.indexOf("\"rhs\":{", k_bop); if k_rhsb < 0 { return null }
local k_t_int = s.indexOf("\"type\":\"Int\"", k_rhsb)
if k_t_int >= 0 {
step = Scan.read_value_int_after(s, k_t_int)
} else {
local k_t_var = s.indexOf("\"type\":\"Var\"", k_rhsb)
if k_t_var < 0 { return null }
local vname = Scan.read_quoted_after_key(s, k_rhsb, "name"); if vname == null { return null }
step = PatternUtilBox.find_local_int_before(s, vname, k_bop)
}
}
if step == null { return null }
if bop_minus {
// Encode subtraction as Add with negative step
local si = JsonFragBox._str_to_int(step)
step = StringHelpers.int_to_str(0 - si)
}
// limit normalized above
// Build via LoopFormBox.build2 ({ mode:"count", init, limit, step, cmp })
local opts = new MapBox()
opts.set("mode", "count")
opts.set("init", init)
opts.set("limit", limit)
opts.set("step", step)
opts.set("cmp", cmp)
return LoopFormBox.build2(opts)
}
}

View File

@ -2,35 +2,72 @@
// Notes: minimal scanner that extracts limit N from Program(JSON v0) Loop cond rhs Int.
using selfhost.shared.mir.loopform as LoopFormBox
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
using ProgScanBox as Scan
using selfhost.shared.common.string_helpers as StringHelpers
using selfhost.shared.json.utils.json_frag as JsonFragBox
using "hako.mir.builder.internal.loop_scan" as LoopScanBox
static box LowerLoopSimpleBox {
try_lower(program_json) {
local s = "" + program_json
// Find Loop with cond Compare op '<' and rhs Int value
// Find Loop with cond Compare and normalize to canonical (<) with dynamic var name
local k_loop = s.indexOf("\"type\":\"Loop\"")
if k_loop < 0 { return null }
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_loop)
if k_cmp < 0 { return null }
// op must be '<'
local k_op = s.indexOf("\"op\":\"<\"", k_cmp)
if k_op < 0 { return null }
// rhs Int value
local k_rhs = s.indexOf("\"rhs\":{", k_cmp)
if k_rhs < 0 { return null }
local k_ti = s.indexOf("\"type\":\"Int\"", k_rhs)
if k_ti < 0 { return null }
// Scan numeric after "value":
local k_v = s.indexOf("\"value\":", k_ti)
if k_v < 0 { return null }
local i = k_v + 8
// skip spaces
loop(i < s.length()) { if s.substring(i,i+1) != " " { break } i = i + 1 }
local j = i
if j < s.length() && s.substring(j,j+1) == "-" { j = j + 1 }
local had = 0
loop(j < s.length()) { local ch = s.substring(j,j+1); if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break } }
if had == 0 { return null }
local limit = s.substring(i, j)
// discover loop var name from cond (lhs or rhs Var)
local varname = LoopScanBox.find_loop_var_name(s, k_cmp)
if varname == null { return null }
// op: accept '<' / '<=' as-is, '!=' (with var on lhs) as '<', and swapped '>' / '>=' (with var on rhs)
local op = Scan.read_quoted_after_key(s, k_cmp, "op")
if op == null { return null }
// Determine where Var(varname) is and extract the Int from the opposite side
local has_lhs_i = s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", k_cmp) >= 0
local has_rhs_i = s.indexOf("\"rhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", k_cmp) >= 0
if !has_lhs_i && !has_rhs_i { return null }
local swapped = 0
local limit = null
if has_lhs_i {
// rhs Int value
local k_rhs = s.indexOf("\"rhs\":{", k_cmp); if k_rhs < 0 { return null }
local k_ti = s.indexOf("\"type\":\"Int\"", k_rhs); if k_ti < 0 { return null }
limit = Scan.read_value_int_after(s, k_ti)
if limit == null { return null }
} else {
// Var is on rhs; lhs must be Int
swapped = 1
local k_lhs = s.indexOf("\"lhs\":{", k_cmp); if k_lhs < 0 { return null }
local k_ti2 = s.indexOf("\"type\":\"Int\"", k_lhs); if k_ti2 < 0 { return null }
limit = Scan.read_value_int_after(s, k_ti2)
if limit == null { return null }
}
// Normalize to canonical '<' with possible +1 adjustment
if swapped == 0 {
if op == "<" {
// ok
} else if op == "<=" {
limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1)
} else if op == "!=" {
// With init=0 and step=1 counting loop, i != L is equivalent to i < L
// keep limit as-is
} else {
return null
}
} else {
// swapped: we expect op to be '>' or '>='
if op == ">" {
// L > i ≡ i < L
} else if op == ">=" {
// L >= i ≡ i <= L ≡ i < (L+1)
limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1)
} else {
return null
}
}
// Delegate to shared loop form builder (counting mode) via build2
local opts = new MapBox()

View File

@ -11,29 +11,57 @@
using "hako.mir.builder.internal.prog_scan" as ProgScanBox
using ProgScanBox as Scan
using selfhost.shared.mir.loopform as LoopFormBox
using selfhost.shared.common.string_helpers as StringHelpers
using selfhost.shared.json.utils.json_frag as JsonFragBox
using "hako.mir.builder.internal.loop_scan" as LoopScanBox
static box LowerLoopSumBcBox {
try_lower(program_json) {
local s = "" + program_json
// Loop and Compare(i < Int limit)
local trace = env.get("HAKO_MIR_BUILDER_TRACE_SUMBC")
if trace != null && ("" + trace) == "1" {
print("[sum_bc] enter lower")
}
// Loop and Compare normalize to canonical (<) with dynamic var name
local k_loop = s.indexOf("\"type\":\"Loop\"")
if k_loop < 0 { return null }
local k_cmp = s.indexOf("\"type\":\"Compare\"", k_loop)
if k_cmp < 0 { return null }
// op "<"
// discover loop var name from cond (lhs or rhs Var)
local varname = null
{
local kl = s.indexOf("\"lhs\":{", k_cmp); local kr = s.indexOf("\"rhs\":{", k_cmp)
if kl >= 0 && s.indexOf("\"type\":\"Var\"", kl) >= 0 { varname = Scan.read_quoted_after_key(s, kl, "name") }
if varname == null && kr >= 0 && s.indexOf("\"type\":\"Var\"", kr) >= 0 { varname = Scan.read_quoted_after_key(s, kr, "name") }
}
if varname == null { return null }
if trace != null && ("" + trace) == "1" {
print("[sum_bc] var=" + varname)
}
// op: accept '<'/'<=' with var on lhs; also accept swapped '>'/'>=' with var on rhs
local op = Scan.read_quoted_after_key(s, k_cmp, "op")
if op == null || op != "<" { return null }
// lhs must mention Var("i"); we check weakly by searching name:"i"
if s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", k_cmp) < 0 { return null }
// rhs Int limit
local k_rhs = s.indexOf("\"rhs\":{", k_cmp)
if k_rhs < 0 { return null }
local k_ti = s.indexOf("\"type\":\"Int\"", k_rhs)
if k_ti < 0 { return null }
local limit = Scan.read_value_int_after(s, k_ti)
if limit == null { return null }
if op == null { return null }
local has_lhs_i = s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", k_cmp) >= 0
local has_rhs_i = s.indexOf("\"rhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", k_cmp) >= 0
if !has_lhs_i && !has_rhs_i { return null }
local limit = null
if has_lhs_i {
if op != "<" && op != "<=" { return null }
// rhs Int limit
local k_rhs = s.indexOf("\"rhs\":{", k_cmp); if k_rhs < 0 { return null }
local k_ti = s.indexOf("\"type\":\"Int\"", k_rhs); if k_ti < 0 { return null }
limit = Scan.read_value_int_after(s, k_ti); if limit == null { return null }
if op == "<=" { limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1) }
} else {
// swapped: Int on lhs, Var i on rhs, op should be '>' or '>='
if op != ">" && op != ">=" { return null }
local k_lhs = s.indexOf("\"lhs\":{", k_cmp); if k_lhs < 0 { return null }
local k_ti2 = s.indexOf("\"type\":\"Int\"", k_lhs); if k_ti2 < 0 { return null }
limit = Scan.read_value_int_after(s, k_ti2); if limit == null { return null }
if op == ">=" { limit = StringHelpers.int_to_str(JsonFragBox._str_to_int(limit) + 1) }
}
// Break sentinel: If(cond Compare i==X) then Break
// Break sentinel: If(cond Compare var==X or X==var) then Break
local break_value = null
{
local kb = s.indexOf("\"type\":\"Break\"", k_loop)
@ -43,14 +71,24 @@ static box LowerLoopSumBcBox {
if kbc >= 0 {
// Ensure op=="==" and lhs Var i
local bop = Scan.read_quoted_after_key(s, kbc, "op")
if bop != null && bop == "==" && s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", kbc) >= 0 {
local kbi = s.indexOf("\"type\":\"Int\"", kbc)
if kbi >= 0 { break_value = Scan.read_value_int_after(s, kbi) }
if bop != null && bop == "==" {
local lhs_i = s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", kbc) >= 0
local rhs_i = s.indexOf("\"rhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", kbc) >= 0
if lhs_i {
local kbi = s.indexOf("\"type\":\"Int\"", s.indexOf("\"rhs\":{", kbc))
if kbi >= 0 { break_value = Scan.read_value_int_after(s, kbi) }
} else if rhs_i {
local kbi2 = s.indexOf("\"type\":\"Int\"", s.indexOf("\"lhs\":{", kbc))
if kbi2 >= 0 { break_value = Scan.read_value_int_after(s, kbi2) }
}
} else if bop != null && bop == "!=" {
// Delegate to loop-scan helper for '!=' + else [Break]
if break_value == null { break_value = LoopScanBox.extract_ne_else_sentinel_value(s, "Break", k_loop, varname) }
}
}
}
}
// Continue sentinel: If(cond Compare i==Y) then Continue
// Continue sentinel: If(cond Compare var==Y or Y==var) then Continue
local skip_value = null
{
local kc = s.indexOf("\"type\":\"Continue\"", k_loop)
@ -58,9 +96,19 @@ static box LowerLoopSumBcBox {
local kcc = s.lastIndexOf("\"type\":\"Compare\"", kc)
if kcc >= 0 {
local cop = Scan.read_quoted_after_key(s, kcc, "op")
if cop != null && cop == "==" && s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"i\"}", kcc) >= 0 {
local kci = s.indexOf("\"type\":\"Int\"", kcc)
if kci >= 0 { skip_value = Scan.read_value_int_after(s, kci) }
if cop != null && cop == "==" {
local lhs_i2 = s.indexOf("\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", kcc) >= 0
local rhs_i2 = s.indexOf("\"rhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", kcc) >= 0
if lhs_i2 {
local kci = s.indexOf("\"type\":\"Int\"", s.indexOf("\"rhs\":{", kcc))
if kci >= 0 { skip_value = Scan.read_value_int_after(s, kci) }
} else if rhs_i2 {
local kci2 = s.indexOf("\"type\":\"Int\"", s.indexOf("\"lhs\":{", kcc))
if kci2 >= 0 { skip_value = Scan.read_value_int_after(s, kci2) }
}
} else if cop != null && cop == "!=" {
// Delegate to loop-scan helper for '!=' + else [Continue]
if skip_value == null { skip_value = LoopScanBox.extract_ne_else_sentinel_value(s, "Continue", k_loop, varname) }
}
}
}
@ -70,12 +118,23 @@ static box LowerLoopSumBcBox {
if skip_value == null { skip_value = 2 }
if break_value == null { break_value = limit }
// Trace detected values
if trace != null && ("" + trace) == "1" {
local skip_str = "" + skip_value
local break_str = "" + break_value
local limit_str = "" + limit
print("[sum_bc] limit=" + limit_str + " skip=" + skip_str + " break=" + break_str)
}
// Use build2 map form for clarity
local opts = new MapBox()
opts.set("mode", "sum_bc")
opts.set("limit", limit)
opts.set("skip", skip_value)
opts.set("break", break_value)
if trace != null && ("" + trace) == "1" {
print("[sum_bc] building MIR with LoopFormBox")
}
return LoopFormBox.build2(opts)
}
}

View File

@ -1,9 +1,27 @@
// pattern_util_box.hako — Shared utilities for MirBuilder lowers
using selfhost.shared.json.utils.json_frag as JsonFragBox
using selfhost.shared.common.string_helpers as StringHelpers
static box PatternUtilBox {
map_cmp(sym) { if sym=="<" {return "Lt"} if sym==">" {return "Gt"} if sym=="<=" {return "Le"} if sym==">=" {return "Ge"} if sym=="==" {return "Eq"} if sym=="!=" {return "Ne"} return null }
// Normalize limit for canonical (i < limit) form.
// When swapped==0, expects op in {'<','<='}; when swapped==1 (Int on lhs, Var on rhs), expects op in {'>','>='}.
// Returns adjusted limit string or null if unsupported.
normalize_limit_for_lt(op, swapped, limit_str) {
local op1 = "" + op
local ls = "" + limit_str
if swapped == 0 {
if op1 == "<" { return ls }
if op1 == "<=" { return StringHelpers.int_to_str(JsonFragBox._str_to_int(ls) + 1) }
if op1 == "!=" { return ls } // safe for count(init=0,step=1)
return null
} else {
if op1 == ">" { return ls }
if op1 == ">=" { return StringHelpers.int_to_str(JsonFragBox._str_to_int(ls) + 1) }
return null
}
}
find_local_int_before(s, name, before_pos) {
local pos=0; local last=-1
loop(true){ local k=JsonFragBox.index_of_from(s, "\"type\":\"Local\"",pos); if k<0||k>=before_pos{break}; local kn=JsonFragBox.index_of_from(s, "\"name\":\"",k); if kn>=0{ local ii=kn+8; local nn=s.length(); local jj=ii; loop(jj<nn){ if s.substring(jj,jj+1)=="\"" {break} jj=jj+1 } if s.substring(ii,jj)==name { last=k } } pos=k+1 }