// 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 (op, swapped, limit) → (cmp, limit') where cmp in {Lt, Le, Gt, Ge} // swapped=0: i ? L, swapped=1: L ? i normalize_cmp_limit(op, swapped, limit_str) { local op1 = "" + op local ls = "" + limit_str if swapped == 0 { if op1 == "<" { return "Lt:" + ls } if op1 == "<=" { return "Lt:" + StringHelpers.int_to_str(JsonFragBox._str_to_int(ls) + 1) } if op1 == ">" { return "Gt:" + ls } if op1 == ">=" { return "Ge:" + ls } if op1 == "!=" { return "Lt:" + ls } // count(init=0,step=1) 前提の近似 return null } else { if op1 == ">" { return "Lt:" + ls } if op1 == ">=" { return "Lt:" + StringHelpers.int_to_str(JsonFragBox._str_to_int(ls) + 1) } if op1 == "<" { return "Gt:" + ls } if op1 == "<=" { return "Ge:" + ls } if op1 == "!=" { return "Lt:" + ls } // 安全近似(入替側) 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 before_pos { next = before_pos } local ki = s.indexOf("\"type\":\"Int\"", last) if ki < 0 || ki >= next { return null } local kv = s.indexOf("\"value\":", ki) if kv < 0 || kv >= next { return null } return JsonFragBox.read_int_after(s, kv+8) } find_local_bool_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 before_pos { next = before_pos } local kb = s.indexOf("\"type\":\"Bool\"", last) if kb < 0 || kb >= next { return null } local kv = s.indexOf("\"value\":", kb) if kv < 0 || kv >= next { return null } // JSON v0 uses 0/1 for bool values, not true/false literals return JsonFragBox.read_int_after(s, kv+8) } // Find the last Local(Int) before a given position (name-agnostic). // Returns string digits or null when not found. find_any_local_int_before(s, before_pos) { local pos = 0; local last_k = -1; local n = ("" + s).length() loop(true) { local k = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", pos) if k < 0 || k >= before_pos { break } last_k = k pos = k + 1 } if last_k < 0 { return null } // Limit search to the next Local or before_pos local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last_k + 1) if next < 0 || next > before_pos { next = before_pos } // Constrain to Local.expr → Int local ti = ("" + s).indexOf("\"expr\":{\"type\":\"Int\"", last_k) if ti < 0 || ti >= next { return null } local kv = ("" + s).indexOf("\"value\":", ti) if kv < 0 || kv >= next { return null } return JsonFragBox.read_int_after(s, kv + 8) } // Find the last Local(String) before a given position (name-agnostic). // Returns raw string (unquoted) or null when not found. find_any_local_string_before(s, before_pos) { local pos = 0; local last_k = -1 loop(true) { local k = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", pos) if k < 0 || k >= before_pos { break } last_k = k pos = k + 1 } if last_k < 0 { return null } local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last_k + 1) if next < 0 || next > before_pos { next = before_pos } // Constrain to Local.expr → String local ts = ("" + s).indexOf("\"expr\":{\"type\":\"String\"", last_k) if ts < 0 || ts >= next { return null } local kv = ("" + s).indexOf("\"value\":", ts) if kv < 0 || kv >= next { return null } return JsonFragBox.read_string_after(s, kv + 8) } }