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:
@ -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 } }
|
||||
|
||||
83
lang/src/mir/builder/internal/loop_scan_box.hako
Normal file
83
lang/src/mir/builder/internal/loop_scan_box.hako
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user