fix(mir/builder): use function-local ValueId throughout MIR builder

Phase 25.1b: Complete SSA fix - eliminate all global ValueId usage in function contexts.

Root cause: ~75 locations throughout MIR builder were using global value
generator (self.value_gen.next()) instead of function-local allocator
(f.next_value_id()), causing SSA verification failures and runtime
"use of undefined value" errors.

Solution:
- Added next_value_id() helper that automatically chooses correct allocator
- Fixed 19 files with ~75 occurrences of ValueId allocation
- All function-context allocations now use function-local IDs

Files modified:
- src/mir/builder/utils.rs: Added next_value_id() helper, fixed 8 locations
- src/mir/builder/builder_calls.rs: 17 fixes
- src/mir/builder/ops.rs: 8 fixes
- src/mir/builder/stmts.rs: 7 fixes
- src/mir/builder/emission/constant.rs: 6 fixes
- src/mir/builder/rewrite/*.rs: 10 fixes
- + 13 other files

Verification:
- cargo build --release: SUCCESS
- Simple tests with NYASH_VM_VERIFY_MIR=1: Zero undefined errors
- Multi-parameter static methods: All working

Known remaining: ValueId(22) in Stage-B (separate issue to investigate)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-17 00:48:18 +09:00
parent 35842503e6
commit eadde8d1dd
59 changed files with 1603 additions and 370 deletions

View File

@ -11,6 +11,7 @@
// - HAKO_MIR_BUILDER_DELEGATE=1 — delegate to Runner (--program-json-to-mir)
// - HAKO_MIR_BUILDER_INTERNAL=0/1 — internal lowers gate既定=1
// - HAKO_MIR_BUILDER_REGISTRY=0/1 — pattern registry gate既定=1
// - HAKO_SELFHOST_NO_DELEGATE=1 — selfhost-first internal 専用プロファイルdelegate を完全に無効化)
//
// Phase 22.0: Hakofirstregistryを既定ONにする。必要なら 0 を明示して無効化する。
@ -432,6 +433,17 @@ static box MirBuilderBox {
return null
}
}
// Selfhost-first profile: when explicitly disabled, do not delegate even if
// HAKO_MIR_BUILDER_DELEGATE=1 が立っていても env.mirbuilder.emit には逃げない。
{
local no_delegate = env.get("HAKO_SELFHOST_NO_DELEGATE")
if no_delegate != null && ("" + no_delegate) == "1" {
if env.get("HAKO_SELFHOST_TRACE") == "1" {
print("[mirbuilder/delegate/disabled] HAKO_SELFHOST_NO_DELEGATE=1; internal-only mode")
}
return null
}
}
// Delegate-first policy (Phase 20.34 Milestone A)
local d = env.get("HAKO_MIR_BUILDER_DELEGATE")
if d != null && ("" + d) == "1" {

View File

@ -23,6 +23,7 @@ Toggles
- `HAKO_MIR_BUILDER_INTERNAL=0/1` — internal lowers gate既定=1
- `HAKO_MIR_BUILDER_REGISTRY=0/1` — pattern registry gate既定=1
- `HAKO_MIR_BUILDER_DELEGATE=1` — use Runner/extern provider (`env.mirbuilder.emit`) 経由で Program→MIR
- `HAKO_SELFHOST_NO_DELEGATE=1` — selfhost-first 時に delegate 経路を完全無効化し、internal lowers のみで成否を判定する
- `HAKO_MIR_BUILDER_FUNCS=1` — enable defs lowering via `FuncLoweringBox.lower_func_defs`
- `HAKO_MIR_BUILDER_METHODIZE=1` — enable call→mir_call(Method) rewrite after MIR 生成
- `HAKO_MIR_BUILDER_JSONFRAG_NORMALIZE=1` — apply JsonFrag normalizer to selfhost/provider output

View File

@ -94,7 +94,17 @@ static box FuncBodyBasicLowerBox {
method _build_func_name(box_name, func_name, params_arr) {
if box_name == null || func_name == null { return null }
local arity = 0
if params_arr != null { arity = JsonFragBox._str_to_int("" + params_arr.length()) }
if params_arr != null {
local n = JsonFragBox._str_to_int("" + params_arr.length())
// StageB defs for static box methods include implicit "me" 先頭引数。
// MIR 側の関数名は「ユーザー引数の個数」で表現したいので、先頭が "me" の場合は引数数から 1 を引く。
if n > 0 && ("" + params_arr.get(0)) == "me" {
arity = n - 1
} else {
arity = n
}
if arity < 0 { arity = 0 }
}
local suffix = "/" + arity
return ("" + box_name) + "." + ("" + func_name) + suffix
}
@ -509,10 +519,10 @@ static box FuncBodyBasicLowerBox {
local result_reg = next_reg
next_reg = next_reg + 1
// Box type 判定(現段階では ArrayBox.size/get と StringBox.length のみ)
// Box type 判定(現段階では ArrayBox.size/get/push と StringBox.length のみ)
local box_type = null
local kind = ""
if mname == "size" || mname == "get" {
if mname == "size" || mname == "get" || mname == "push" {
box_type = "ArrayBox"
kind = "array"
} else if mname == "length" {
@ -549,7 +559,7 @@ static box FuncBodyBasicLowerBox {
if out != null { return me._rebind(out, func_name, box_name, params_arr, "[funcs/basic:loop.sum_bc]") } }
// 3) 次に multi_carrier 用 lower を試すfibonacci風
{ local out_mc = LowerLoopMultiCarrierBox.try_lower(body_json)
{ local out_mc = LowerLoopMultiCarrierBox.try_lower(body_json, params_arr)
if out_mc != null { return me._rebind(out_mc, func_name, box_name, params_arr, "[funcs/basic:loop.multi_carrier]") } }
// 4) 最後に simple loop 用 lower を試す(単純カウンタ)

View File

@ -31,6 +31,14 @@ static box CliRunLowerBox {
return null
}
// Shape が既知パターンと一致した時点で、入口検出タグと粗い形状タグを出しておく。
if env.get("HAKO_SELFHOST_TRACE") == "1" {
// Stage1 CLI entry: Main.main → HakoCli.run/2argv 引数付き)
print("[builder/cli:entry_detected] main=Main.main run=" + box_name + "." + func_name + "/2")
// run/build/emit/check の 4 分岐を想定した形状タグ(将来は CliRunShapeScannerBox からの情報に揃える)。
print("[builder/cli:run_shape] has_run=1 branches=4")
}
// 既定では MIR 生成はトグルで有効化freeze ポリシーに従い既定OFF
local flag = env.get("HAKO_MIR_BUILDER_CLI_RUN")
if flag == null { flag = "0" }

View File

@ -120,7 +120,17 @@ static box ExternCallLowerBox {
method _build_func_name(box_name, func_name, params_arr) {
if box_name == null || func_name == null { return null }
local arity = 0
if params_arr != null { arity = JsonFragBox._str_to_int("" + params_arr.length()) }
if params_arr != null {
local n = JsonFragBox._str_to_int("" + params_arr.length())
// HakoCli.* など static box メソッドの defs では先頭に暗黙の "me" が入る。
// 関数名はユーザー引数の個数で表現したいので、先頭が "me" の場合は 1 引いておく。
if n > 0 && ("" + params_arr.get(0)) == "me" {
arity = n - 1
} else {
arity = n
}
if arity < 0 { arity = 0 }
}
return ("" + box_name) + "." + ("" + func_name) + "/" + arity
}
@ -138,4 +148,3 @@ static box ExternCallLowerBox {
return params_json
}
}

View File

@ -3,6 +3,9 @@
// construction without touching all callers.
using "hako.mir.builder.internal.builder_config" as BuilderConfigBox
using selfhost.shared.common.string_helpers as StringHelpers
using selfhost.shared.common.box_helpers as BoxHelpers
using selfhost.shared.mir.json_emit as JsonEmitBox
static box LoopOptsBox {
new_map() {
@ -15,7 +18,6 @@ static box LoopOptsBox {
// build2: envでJsonFrag直組立を選択既定は LoopFormBox.build2 に委譲)
build2(opts) {
using selfhost.shared.mir.loopform as LoopFormBox
using selfhost.shared.common.string_helpers as StringHelpers
using "hako.mir.builder.internal.jsonfrag_normalizer" as JsonFragNormalizerBox
// Force toggle: always return JsonFrag (dev/test override)
if BuilderConfigBox.loop_force_jsonfrag_on() == 1 {
@ -80,7 +82,219 @@ static box LoopOptsBox {
}
return mir
}
// Default: delegate
return LoopFormBox.build2(opts)
// Default: delegate to LoopForm and emit canonical JSON via shared JsonEmitBox
local module = LoopFormBox.build2(opts)
if module == null { return null }
local json = JsonEmitBox.to_json(module)
if BuilderConfigBox.jsonfrag_normalize_on() == 1 { json = JsonFragNormalizerBox.normalize_all(json) }
local mode2 = BuilderConfigBox.loop_adapter_return_mode()
if mode2 == "map" {
local m2 = me.new_map()
m2 = me.put(m2, "mir", json)
return m2
}
return json
}
method _module_to_json(module) {
if module == null { return "{}" }
local kind = module.get("kind")
if kind == null { kind = "MIR" }
local schema = module.get("schema_version")
if schema == null { schema = "1.0" }
local funcs = module.get("functions")
local body = me._emit_functions(funcs)
return "{\"kind\":" + me._quote(kind) + ",\"schema_version\":" + me._quote(schema) + ",\"functions\":[" + body + "]}"
}
method _emit_functions(funcs) {
if funcs == null { return "" }
local len = me._array_len(funcs)
local out = ""
local i = 0
loop(i < len) {
local func = funcs.get(i)
if func != null {
if out != "" { out = out + "," }
out = out + me._emit_function(func)
}
i = i + 1
}
return out
}
method _emit_function(func) {
if func == null { return "{\"name\":\"main\",\"blocks\":[]}" }
local name = func.get("name")
if name == null { name = "main" }
local params = func.get("params")
local blocks = func.get("blocks")
local out = "{\"name\":" + me._quote(name)
if params != null && BoxHelpers.is_array(params) {
out = out + ",\"params\":" + me._emit_params(params)
}
if blocks != null {
out = out + ",\"blocks\":[" + me._emit_blocks(blocks) + "]"
} else {
out = out + ",\"blocks\":[]"
}
out = out + "}"
return out
}
method _emit_params(arr) {
if arr == null { return "[]" }
local len = me._array_len(arr)
local out = ""
local i = 0
loop(i < len) {
if out != "" { out = out + "," }
out = out + me._quote(arr.get(i))
i = i + 1
}
return "[" + out + "]"
}
method _emit_blocks(blocks) {
local len = me._array_len(blocks)
local out = ""
local i = 0
loop(i < len) {
local block = blocks.get(i)
if block != null {
if out != "" { out = out + "," }
out = out + me._emit_block(block)
}
i = i + 1
}
return out
}
method _emit_block(block) {
local id = block.get("id")
local insts = block.get("instructions")
local body = me._emit_insts(insts)
return "{\"id\":" + me._i64(id) + ",\"instructions\":[" + body + "]}"
}
method _emit_insts(insts) {
if insts == null { return "" }
local len = me._array_len(insts)
local out = ""
local i = 0
loop(i < len) {
local inst = insts.get(i)
if inst != null {
if out != "" { out = out + "," }
out = out + me._emit_inst(inst)
}
i = i + 1
}
return out
}
method _emit_inst(inst) {
local op = "" + inst.get("op")
if op == "const" {
return "{\"op\":\"const\",\"dst\":" + me._i64(inst.get("dst")) + ",\"value\":" + me._emit_box_value(inst.get("value")) + "}"
}
if op == "ret" {
return "{\"op\":\"ret\",\"value\":" + me._i64(inst.get("value")) + "}"
}
if op == "jump" {
return "{\"op\":\"jump\",\"target\":" + me._i64(inst.get("target")) + "}"
}
if op == "compare" {
local cmp = inst.get("cmp")
if cmp == null { cmp = inst.get("operation") }
return "{\"op\":\"compare\",\"operation\":" + me._quote(cmp) + ",\"lhs\":" + me._i64(inst.get("lhs")) + ",\"rhs\":" + me._i64(inst.get("rhs")) + ",\"dst\":" + me._i64(inst.get("dst")) + "}"
}
if op == "branch" {
return "{\"op\":\"branch\",\"cond\":" + me._i64(inst.get("cond")) + ",\"then\":" + me._i64(inst.get("then")) + ",\"else\":" + me._i64(inst.get("else")) + "}"
}
if op == "phi" {
return "{\"op\":\"phi\",\"dst\":" + me._i64(inst.get("dst")) + ",\"incoming\":" + me._emit_phi(inst.get("incoming")) + "}"
}
if op == "binop" {
local kind = inst.get("op_kind")
if kind == null { kind = inst.get("operation") }
return "{\"op\":\"binop\",\"operation\":" + me._quote(kind) + ",\"lhs\":" + me._i64(inst.get("lhs")) + ",\"rhs\":" + me._i64(inst.get("rhs")) + ",\"dst\":" + me._i64(inst.get("dst")) + "}"
}
if op == "mir_call" {
local payload = inst.get("mir_call")
local callee = payload.get("callee")
local callee_json = "{\"type\":\"Method\",\"box_name\":" + me._quote(callee.get("box_name")) + ",\"method\":" + me._quote(callee.get("method")) + ",\"receiver\":" + me._i64(callee.get("receiver")) + "}"
local args_json = me._emit_args(payload.get("args"))
return "{\"op\":\"mir_call\",\"dst\":" + me._i64(inst.get("dst")) + ",\"mir_call\":{\"callee\":" + callee_json + ",\"args\":" + args_json + ",\"effects\":[]}}"
}
if op == "copy" {
return "{\"op\":\"copy\",\"src\":" + me._i64(inst.get("src")) + ",\"dst\":" + me._i64(inst.get("dst")) + "}"
}
return "{\"op\":" + me._quote(op) + "}"
}
method _emit_phi(values) {
if values == null { return "[]" }
local len = me._array_len(values)
local out = ""
local i = 0
loop(i < len) {
local item = values.get(i)
if item != null {
if out != "" { out = out + "," }
out = out + "{\"block\":" + me._i64(item.get("block")) + ",\"value\":" + me._i64(item.get("value")) + "}"
}
i = i + 1
}
return "[" + out + "]"
}
method _emit_args(args) {
if args == null { return "[]" }
local len = me._array_len(args)
local out = ""
local i = 0
loop(i < len) {
if out != "" { out = out + "," }
out = out + me._i64(args.get(i))
i = i + 1
}
return "[" + out + "]"
}
method _emit_box_value(val) {
if val == null { return "{\"type\":\"i64\",\"value\":0}" }
local ty = "i64"
if BoxHelpers.is_map(val) {
local ty_box = BoxHelpers.map_get(val, "type")
if ty_box != null { ty = "" + ty_box }
local inner = BoxHelpers.value_i64(val)
return "{\"type\":" + me._quote(ty) + ",\"value\":" + me._i64(inner) + "}"
}
// Non-MapBox: treat as plain numeric value
local inner2 = BoxHelpers.value_i64(val)
return "{\"type\":" + me._quote(ty) + ",\"value\":" + me._i64(inner2) + "}"
}
method _array_len(arr) {
// Delegate to shared helper to support both builtin ArrayBox.length (IntegerBox)
// and MapBox-wrapped length from plugins.
return BoxHelpers.array_len(arr)
}
method _i64(val) {
if val == null { return "0" }
// Prefer MapBox-wrapped integers when available, otherwise fall back to direct value.
if BoxHelpers.is_map(val) {
local inner = BoxHelpers.map_get(val, "value")
if inner != null { return "" + inner }
return "0"
}
return "" + val
}
method _quote(val) {
if val == null { return "\"\"" }
return StringHelpers.json_quote("" + val)
}
}

View File

@ -10,11 +10,11 @@ static box LoopScanBox {
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 kn >= 0 { varname = JsonFragBox.read_string_after(s, kn + 7) }
}
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) }
if kn2 >= 0 { varname = JsonFragBox.read_string_after(s, kn2 + 7) }
}
return varname
}

View File

@ -16,7 +16,7 @@ using "hako.mir.builder.internal.builder_config" as BuilderConfigBox
static box LowerLoopMultiCarrierBox {
// Try to recognize and lower a multi-carrier loop in Program(JSON v0).
// Pattern: loop with multiple Local/Assign indicating carried state (fibonacci-style)
method try_lower(program_json) {
method try_lower(program_json, params_arr) {
if program_json == null { return null }
local s = "" + program_json
@ -31,69 +31,183 @@ static box LowerLoopMultiCarrierBox {
local varname = LoopScanBox.find_loop_var_name(s, k_cmp)
if varname == null { return null }
// 3) Extract limit from Compare (i < limit pattern)
local k_op = JsonFragBox.index_of_from(s, "\"op\":", k_cmp)
if k_op < 0 { return null }
local op = JsonFragBox.read_string_after(s, k_op + 5)
if op == null { return null }
// Check for lhs Var pattern (i on left side)
local has_lhs_i = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Var\",\"name\":\"" + varname + "\"}", k_cmp) >= 0
if !has_lhs_i { return null }
// Extract limit from rhs Int
local k_rhs = JsonFragBox.index_of_from(s, "\"rhs\":{", k_cmp)
if k_rhs < 0 { return null }
local k_ti = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", k_rhs)
if k_ti < 0 { return null }
local k_v = JsonFragBox.index_of_from(s, "\"value\":", k_ti)
if k_v < 0 { return null }
local limit = JsonFragBox.read_int_after(s, k_v + 8)
if limit == null { return null }
// 4) Check for multiple Local/Assign in Loop body (multi-carrier indicator)
local k_body = JsonFragBox.index_of_from(s, "\"body\":[", k_loop)
if k_body < 0 { return null }
// Count Local declarations within this Loop's body
local local_count = 0
local search_pos = k_body
loop(local_count < 5) {
local k_local = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", search_pos)
if k_local < 0 { break }
// Ensure it's within the current Loop body (not a nested Loop)
local next_loop = JsonFragBox.index_of_from(s, "\"type\":\"Loop\"", k_local)
if next_loop >= 0 && next_loop < k_local + 200 { break }
local_count = local_count + 1
search_pos = k_local + 1
}
// Multi-carrier requires at least 2 additional variables (besides loop var)
// e.g. fibonacci: a, b (+ implicit i)
if local_count < 2 {
// 3) Extract limit information (accept Int literal or param Var)
local limit_info = me._extract_limit_info(s, k_cmp, varname, params_arr)
if limit_info == null {
if BuilderConfigBox.trace_enabled() == 1 {
print("[mirbuilder/internal/loop:multi_carrier:insufficient_carriers:count=" + local_count + "]")
print("[mirbuilder/internal/loop:multi_carrier:limit:unsupported]")
}
return null
}
local limit_kind = "" + limit_info.get("kind")
local limit_value = limit_info.get("value")
local limit_param_reg = JsonFragBox._str_to_int("" + limit_info.get("param_reg"))
if limit_kind != "const" && limit_kind != "param" { return null }
// 5) Build opts for multi_count mode
// 4) Collect initial carrier values (Local Int assignments before the Loop)
local carrier_vals = me._collect_carrier_initials(s, k_loop, varname)
if carrier_vals == null || carrier_vals.length() < 2 {
local count = 0
if carrier_vals != null { count = carrier_vals.length() }
print("[mirbuilder/internal/loop:multi_carrier:insufficient_carriers:count=" + count + "]")
return null
}
// 5) Build opts for multi_count mode with detected carriers
local opts = LoopOptsBox.new_map()
opts = LoopOptsBox.put(opts, "mode", "multi_count")
opts = LoopOptsBox.put(opts, "limit", limit)
// Carriers: default to [0, 1] for fibonacci pattern
// TODO: extract initial values from JSON (future enhancement)
local carriers = new ArrayBox()
carriers.push(0)
carriers.push(1)
opts = LoopOptsBox.put(opts, "carriers", carriers)
if BuilderConfigBox.trace_enabled() == 1 {
print("[mirbuilder/internal/loop:multi_carrier:detected:limit=" + limit + ",carriers=2]")
opts = LoopOptsBox.put(opts, "limit_kind", limit_kind)
if limit_kind == "const" {
opts = LoopOptsBox.put(opts, "limit", limit_value)
} else {
opts = LoopOptsBox.put(opts, "limit_param_reg", limit_param_reg)
}
opts = LoopOptsBox.put(opts, "carriers", carrier_vals)
local tag = "[mirbuilder/internal/loop:multi_carrier:detected:limit_kind=" + limit_kind
if limit_kind == "const" {
tag = tag + ",value=" + limit_value
} else {
tag = tag + ",param_reg=" + limit_param_reg
}
tag = tag + ",carriers=" + carrier_vals.length() + "]"
print(tag)
// 6) Delegate to LoopFormBox.build2 via LoopOptsBox
return LoopOptsBox.build2(opts)
}
// Extract initial carrier values (Local Int assignments before Loop body)
method _collect_carrier_initials(body_json, loop_idx, loop_var) {
local vals = new ArrayBox()
if body_json == null { return vals }
local search = 0
loop(true) {
local k_local = JsonFragBox.index_of_from(body_json, "\"type\":\"Local\"", search)
if k_local < 0 || (loop_idx >= 0 && k_local >= loop_idx) { break }
local name_idx = JsonFragBox.index_of_from(body_json, "\"name\":", k_local)
if name_idx < 0 { break }
local name = JsonFragBox.read_string_after(body_json, name_idx + 7)
if name == null { break }
if loop_var != null && name == loop_var {
search = k_local + 1
continue
}
local expr_idx = JsonFragBox.index_of_from(body_json, "\"expr\":{", k_local)
if expr_idx < 0 || (loop_idx >= 0 && expr_idx >= loop_idx) {
search = k_local + 1
continue
}
local int_idx = JsonFragBox.index_of_from(body_json, "\"type\":\"Int\"", expr_idx)
if int_idx < 0 || (loop_idx >= 0 && int_idx >= loop_idx) {
search = k_local + 1
continue
}
local val_idx = JsonFragBox.index_of_from(body_json, "\"value\":", int_idx)
if val_idx < 0 { search = k_local + 1 continue }
local val = JsonFragBox.read_int_after(body_json, val_idx + 8)
if val != null {
vals.push(JsonFragBox._str_to_int("" + val))
}
search = k_local + 1
}
return vals
}
method _extract_limit_info(body_json, cmp_idx, loop_var, params_arr) {
local s = "" + body_json
local has_lhs = JsonFragBox.index_of_from(s, "\"lhs\":{\"type\":\"Var\",\"name\":\"" + loop_var + "\"}", cmp_idx) >= 0
local has_rhs = JsonFragBox.index_of_from(s, "\"rhs\":{\"type\":\"Var\",\"name\":\"" + loop_var + "\"}", cmp_idx) >= 0
local trace_on = BuilderConfigBox.trace_enabled()
if !has_lhs && !has_rhs {
if trace_on == 1 {
print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=no-loop-var var=" + loop_var)
}
return null
}
local target_idx = -1
if has_lhs {
target_idx = JsonFragBox.index_of_from(s, "\"rhs\":{", cmp_idx)
} else {
target_idx = JsonFragBox.index_of_from(s, "\"lhs\":{", cmp_idx)
}
if target_idx < 0 {
if trace_on == 1 {
print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=no-target")
}
return null
}
local type_idx = JsonFragBox.index_of_from(s, "\"type\":\"", target_idx)
if type_idx < 0 {
if trace_on == 1 {
print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=no-type")
}
return null
}
local typ = JsonFragBox.read_string_after(s, type_idx + 7)
if typ == null {
if trace_on == 1 { print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=type-null") }
return null
}
if typ == "Int" {
local val_idx = JsonFragBox.index_of_from(s, "\"value\":", type_idx)
if val_idx < 0 {
if trace_on == 1 { print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=no-int-value") }
return null
}
local val = JsonFragBox.read_int_after(s, val_idx + 8)
if val == null {
if trace_on == 1 { print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=int-parse") }
return null
}
local info = new MapBox()
info.set("kind", "const")
info.set("value", JsonFragBox._str_to_int("" + val))
return info
}
if typ == "Var" {
local name_idx = JsonFragBox.index_of_from(s, "\"name\":\"", type_idx)
if name_idx < 0 {
if trace_on == 1 { print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=no-var-name") }
return null
}
local name = JsonFragBox.read_string_after(s, name_idx + 7)
if name == null {
if trace_on == 1 { print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=var-name-null") }
return null
}
local param_reg = me._resolve_param_reg(params_arr, name)
if trace_on == 1 {
print("[mirbuilder/internal/loop:multi_carrier:limit:param] name=" + name + ",reg=" + param_reg)
}
if param_reg <= 0 {
if trace_on == 1 {
print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=param-unresolved")
}
return null
}
local info2 = new MapBox()
info2.set("kind", "param")
info2.set("param_reg", param_reg)
return info2
}
if trace_on == 1 {
print("[mirbuilder/internal/loop:multi_carrier:limit:scan:fail] reason=type-unsupported typ=" + typ)
}
return null
}
method _resolve_param_reg(params_arr, name) {
if params_arr == null || name == null { return 0 }
local idx = 0
local total = params_arr.length()
loop(idx < total) {
local pname = "" + params_arr.get(idx)
if pname == name {
return JsonFragBox._str_to_int("" + (idx + 1))
}
idx = idx + 1
}
return 0
}
}