- Add HAKO_TRACE_EXECUTION to trace executor route - Rust hv1_inline: stderr [trace] executor: hv1_inline (rust) - Hakovm dispatcher: stdout [trace] executor: hakovm (hako) - test_runner: trace lines for hv1_inline/core/hakovm routes - Add HAKO_VERIFY_SHOW_LOGS and HAKO_DEBUG=1 (enables both) - verify_v1_inline_file() log passthrough with numeric rc extraction - test_runner exports via HAKO_DEBUG - Canary expansion under phase2170 (state spec) - Array: push×5/10 → size, len/length alias, per‑recv/global, flow across blocks - Map: set dup-key non-increment, value_state get/has - run_all.sh: unify, remove SKIPs; all PASS - Docs - ENV_VARS.md: add Debug/Tracing toggles and examples - PLAN.md/CURRENT_TASK.md: mark 21.7 green, add Quickstart lines All changes gated by env vars; default behavior unchanged.
116 lines
5.5 KiB
Plaintext
116 lines
5.5 KiB
Plaintext
// 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<nn){ if s.substring(jj,jj+1)=="\"" {break} jj=jj+1 } if s.substring(ii,jj)==name { last=k } } pos=k+1 }
|
|
if last<0 { return null }
|
|
// Bound the search between this Local and the next Local/before_pos
|
|
local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last + 1)
|
|
if next < 0 || next > 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<nn){ if s.substring(jj,jj+1)=="\"" {break} jj=jj+1 } if s.substring(ii,jj)==name { last=k } } pos=k+1 }
|
|
if last<0 { return null }
|
|
local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last + 1)
|
|
if next < 0 || next > 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)
|
|
}
|
|
}
|