Files
hakorune/lang/src/mir/builder/internal/loop_scan_box.hako
nyash-codex 6055d53eff feat(phase21.5/22.1): MirBuilder JsonFrag refactor + FileBox ring-1 + registry tests
Phase 21.5 (AOT/LLVM Optimization Prep)
- FileBox ring-1 (core-ro) provider: priority=-100, always available, no panic path
  - src/runner/modes/common_util/provider_registry.rs: CoreRoFileProviderFactory
  - Auto-registers at startup, eliminates fallback panic structurally
- StringBox fast path prototypes (length/size optimization)
- Performance benchmarks (C/Python/Hako comparison baseline)

Phase 22.1 (JsonFrag Unification)
- JsonFrag.last_index_of_from() for backward search (VM fallback)
- Replace hand-written lastIndexOf in lower_loop_sum_bc_box.hako
- SentinelExtractorBox for Break/Continue pattern extraction

MirBuilder Refactor (Box → JsonFrag Migration)
- 20+ lower_*_box.hako: Box-heavy → JsonFrag text assembly
- MirBuilderMinBox: lightweight using set for dev env
- Registry-only fast path with [registry:*] tag observation
- pattern_util_box.hako: enhanced pattern matching

Dev Environment & Testing
- Dev toggles: SMOKES_DEV_PREINCLUDE=1 (point-enable), HAKO_MIR_BUILDER_SKIP_LOOPS=1
- phase2160: registry opt-in tests (array/map get/set/push/len) - content verification
- phase2034: rc-dependent → token grep (grep -F based validation)
- run_quick.sh: fast smoke testing harness
- ENV documentation: docs/ENV_VARS.md

Test Results
 quick phase2034: ALL GREEN (MirBuilder internal patterns)
 registry phase2160: ALL GREEN (array/map get/set/push/len)
 rc-dependent tests → content token verification complete
 PREINCLUDE policy: default OFF, point-enable only where needed

Technical Notes
- No INCLUDE by default (maintain minimalism)
- FAIL_FAST=0 in Bring-up contexts only (explicit dev toggles)
- Tag-based route observation ([mirbuilder/min:*], [registry:*])
- MIR structure validation (not just rc parity)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 19:42:42 +09:00

90 lines
4.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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
}
}