// loop_scan_box.hako — If/Compare + then/else 範囲スキャンの小箱 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 = JsonFragBox.index_of_from(s, "\"lhs\":{", k_cmp) local kr = JsonFragBox.index_of_from(s, "\"rhs\":{", k_cmp) if kl >= 0 && JsonFragBox.index_of_from(s, "\"type\":\"Var\"", kl) >= 0 { local kn = JsonFragBox.index_of_from(s, "\"name\":\"", kl) if kn >= 0 { varname = JsonFragBox.read_string_after(s, kn) } } if varname == null && kr >= 0 && JsonFragBox.index_of_from(s, "\"type\":\"Var\"", kr) >= 0 { local kn2 = JsonFragBox.index_of_from(s, "\"name\":\"", kr) if kn2 >= 0 { varname = JsonFragBox.read_string_after(s, kn2) } } 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 = JsonFragBox.index_of_from(s, st, k_loop) if ks < 0 { return null } // 直前の If と Compare を見つける(同一 then/else 内の近傍に限定) local kif = JsonFragBox.last_index_of_from(s, "\"type\":\"If\"", ks) if kif < 0 { return null } local kcmp = JsonFragBox.last_index_of_from(s, "\"type\":\"Compare\"", ks) if kcmp < 0 || kcmp < kif { return null } local kop = JsonFragBox.index_of_from(s, "\"op\":", kcmp); if kop < 0 { return null } local op = JsonFragBox.read_string_after(s, kop + 5); 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 = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", kcmp) >= 0 local has_rhs = JsonFragBox.index_of_from(s, "\"rhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", kcmp) >= 0 if !has_lhs && !has_rhs { return null } if has_lhs { local kr = JsonFragBox.index_of_from(s, "\"rhs\":{", kcmp); if kr < 0 { return null } local kt = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", kr); if kt < 0 { return null } local kv = JsonFragBox.index_of_from(s, "\"value\":", kt); if kv < 0 { return null } local sentinel_val = JsonFragBox.read_int_after(s, kv + 8) // 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 = JsonFragBox.index_of_from(s, "\"lhs\":{", kcmp); if kl < 0 { return null } local kt2 = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", kl); if kt2 < 0 { return null } local kv2 = JsonFragBox.index_of_from(s, "\"value\":", kt2); if kv2 < 0 { return null } local sentinel_val2 = JsonFragBox.read_int_after(s, kv2 + 8) // 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 } }