// mir_vm_min.nyash — Ny製の最小MIR(JSON v0)実行器(const→retのみ) // 目的: M2スケルトン。仕様は既定OFFに影響しない新規アプリのみ。 // 入力: MIR(JSON v0) 文字列。形式例: // { // "functions":[{"name":"main","params":[],"blocks":[{"id":0,"instructions":[ // {"op":"const","dst":1,"value":{"type":"i64","value":42}}, // {"op":"ret","value":1} // ]}]}] // } // 振る舞い: // - M1: 最初の const i64 の値を読み取り print // - M2: const/binop/compare/ret を最小実装(簡易スキャンで安全に解釈) static box MirVmMin { // Public entry used by parity tests (calls into minimal runner) run(mjson) { local v = me._run_min(mjson) print(me._int_to_str(v)) return v } // 最小限のスキャン関数(依存ゼロ版) index_of_from(hay, needle, pos) { if pos < 0 { pos = 0 } local n = hay.length() if pos >= n { return -1 } local m = needle.length() if m <= 0 { return pos } local i = pos local limit = n - m loop (i <= limit) { local seg = hay.substring(i, i + m) if seg == needle { return i } i = i + 1 } return -1 } read_digits(text, pos) { local out = "" local i = pos loop (true) { local s = text.substring(i, i+1) if s == "" { break } if s == "0" || s == "1" || s == "2" || s == "3" || s == "4" || s == "5" || s == "6" || s == "7" || s == "8" || s == "9" { out = out + s i = i + 1 } else { break } } return out } _str_to_int(s) { local i = 0 local n = s.length() local acc = 0 loop (i < n) { local ch = s.substring(i, i+1) if ch == "0" { acc = acc * 10 + 0 i = i + 1 continue } if ch == "1" { acc = acc * 10 + 1 i = i + 1 continue } if ch == "2" { acc = acc * 10 + 2 i = i + 1 continue } if ch == "3" { acc = acc * 10 + 3 i = i + 1 continue } if ch == "4" { acc = acc * 10 + 4 i = i + 1 continue } if ch == "5" { acc = acc * 10 + 5 i = i + 1 continue } if ch == "6" { acc = acc * 10 + 6 i = i + 1 continue } if ch == "7" { acc = acc * 10 + 7 i = i + 1 continue } if ch == "8" { acc = acc * 10 + 8 i = i + 1 continue } if ch == "9" { acc = acc * 10 + 9 i = i + 1 continue } break } return acc } _int_to_str(n) { if n == 0 { return "0" } local v = n local out = "" local digits = "0123456789" loop (v > 0) { local d = v % 10 local ch = digits.substring(d, d+1) out = ch + out v = v / 10 } return out } // MVP: 最初の const i64 の値を抽出 _extract_first_const_i64(text) { if text == null { return 0 } // "op":"const" を探す local p = text.indexOf("\"op\":\"const\"") if p < 0 { return 0 } // そこから "\"value\":{\"type\":\"i64\",\"value\":" を探す local key = "\"value\":{\"type\":\"i64\",\"value\":" local q = me.index_of_from(text, key, p) if q < 0 { return 0 } q = q + key.length() // 連続する数字を読む local digits = me.read_digits(text, q) if digits == "" { return 0 } return me._str_to_int(digits) } // --- M2 追加: 最小 MIR 実行(const/binop/compare/ret) --- _get_map(regs, key) { if regs.has(key) { return regs.get(key) } return 0 } _set_map(regs, key, val) { regs.set(key, val) } _find_int_in(seg, keypat) { local p = seg.indexOf(keypat) if p < 0 { return null } p = p + keypat.length() local i = p local out = "" loop(true) { local ch = seg.substring(i, i+1) if ch == "" { break } if ch == "0" || ch == "1" || ch == "2" || ch == "3" || ch == "4" || ch == "5" || ch == "6" || ch == "7" || ch == "8" || ch == "9" { out = out + ch i = i + 1 } else { break } } if out == "" { return null } return me._str_to_int(out) } _find_str_in(seg, keypat) { local p = seg.indexOf(keypat) if p < 0 { return "" } p = p + keypat.length() local q = me.index_of_from(seg, "\"", p) if q < 0 { return "" } return seg.substring(p, q) } // --- JSON segment helpers (brace/bracket aware, minimal) --- _seek_obj_start(text, from_pos) { // scan backward to the nearest '{' local i = from_pos loop(true) { i = i - 1 if i < 0 { return 0 } local ch = text.substring(i, i+1) if ch == "{" { return i } } return 0 } _seek_obj_end(text, obj_start) { // starting at '{', find matching '}' using depth counter local i = obj_start local depth = 0 loop(true) { local ch = text.substring(i, i+1) if ch == "" { break } if ch == "{" { depth = depth + 1 } else { if ch == "}" { depth = depth - 1 } } if depth == 0 { return i + 1 } i = i + 1 } return i } _seek_array_end(text, array_after_bracket_pos) { // given pos right after '[', find the matching ']' local i = array_after_bracket_pos local depth = 1 loop(true) { local ch = text.substring(i, i+1) if ch == "" { break } if ch == "[" { depth = depth + 1 } else { if ch == "]" { depth = depth - 1 } } if depth == 0 { return i } i = i + 1 } return i } _map_binop_symbol(sym) { if sym == "+" { return "Add" } if sym == "-" { return "Sub" } if sym == "*" { return "Mul" } if sym == "/" { return "Div" } if sym == "%" { return "Mod" } return "" } _map_cmp_symbol(sym) { if sym == "==" { return "Eq" } if sym == "!=" { return "Ne" } if sym == "<" { return "Lt" } if sym == "<=" { return "Le" } if sym == ">" { return "Gt" } if sym == ">=" { return "Ge" } return "" } _eval_binop(kind, a, b) { if kind == "Add" { return a + b } if kind == "Sub" { return a - b } if kind == "Mul" { return a * b } if kind == "Div" { if b == 0 { return 0 } else { return a / b } } if kind == "Mod" { if b == 0 { return 0 } else { return a % b } } return 0 } _eval_cmp(kind, a, b) { if kind == "Eq" { if a == b { return 1 } else { return 0 } } if kind == "Ne" { if a != b { return 1 } else { return 0 } } if kind == "Lt" { if a < b { return 1 } else { return 0 } } if kind == "Gt" { if a > b { return 1 } else { return 0 } } if kind == "Le" { if a <= b { return 1 } else { return 0 } } if kind == "Ge" { if a >= b { return 1 } else { return 0 } } return 0 } // Locate start of instructions array for given block id _block_insts_start(mjson, bid) { local key = "\"id\":" + me._int_to_str(bid) local p = mjson.indexOf(key) if p < 0 { return -1 } local q = me.index_of_from(mjson, "\"instructions\":[", p) if q < 0 { return -1 } // "\"instructions\":[" is 16 chars → return pos right after '[' return q + 16 } _block_insts_end(mjson, insts_start) { // Bound to the end bracket of this block's instructions array return me._seek_array_end(mjson, insts_start) } _run_min(mjson) { local regs = new MapBox() // Control flow: start at block 0, process until ret local bb = 0 loop(true) { local pos = me._block_insts_start(mjson, bb) if pos < 0 { return me._extract_first_const_i64(mjson) } local block_end = me._block_insts_end(mjson, pos) // Single-pass over instructions: segment each op object precisely and evaluate local scan = pos local moved = 0 loop(true) { // find next op field within this block local opos = me.index_of_from(mjson, "\"op\":\"", scan) if opos < 0 || opos >= block_end { break } // find exact JSON object bounds for this instruction // Determine object start as the last '{' between pos..opos local i = pos local obj_start = opos loop(i <= opos) { local ch0 = mjson.substring(i, i+1) if ch0 == "{" { obj_start = i } i = i + 1 } local obj_end = me._seek_obj_end(mjson, obj_start) if obj_end > block_end { obj_end = block_end } local seg = mjson.substring(obj_start, obj_end) // dispatch by op name (v0/v1 tolerant) local opname = me._find_str_in(seg, "\"op\":\"") if opname == "const" { local cdst = me._find_int_in(seg, "\"dst\":") local cval = me._find_int_in(seg, "\"value\":{\"type\":\"i64\",\"value\":") if cdst != null and cval != null { me._set_map(regs, "" + cdst, cval) } } else { if opname == "binop" { local bdst = me._find_int_in(seg, "\"dst\":") local bkind = me._find_str_in(seg, "\"op_kind\":\"") if bkind == "" { bkind = me._map_binop_symbol(me._find_str_in(seg, "\"operation\":\"")) } local blhs = me._find_int_in(seg, "\"lhs\":") local brhs = me._find_int_in(seg, "\"rhs\":") if bdst != null and blhs != null and brhs != null { local a = me._get_map(regs, "" + blhs) local b = me._get_map(regs, "" + brhs) local r = me._eval_binop(bkind, a, b) me._set_map(regs, "" + bdst, r) } } else { if opname == "compare" { local kdst = me._find_int_in(seg, "\"dst\":") local kkind = me._find_str_in(seg, "\"cmp\":\"") if kkind == "" { kkind = me._map_cmp_symbol(me._find_str_in(seg, "\"operation\":\"")) } local klhs = me._find_int_in(seg, "\"lhs\":") local krhs = me._find_int_in(seg, "\"rhs\":") if kdst != null and klhs != null and krhs != null { local a = me._get_map(regs, "" + klhs) local b = me._get_map(regs, "" + krhs) local r = me._eval_cmp(kkind, a, b) me._set_map(regs, "" + kdst, r) } } else { if opname == "jump" { local tgt = me._find_int_in(seg, "\"target\":") if tgt != null { bb = tgt scan = block_end moved = 1 break } } else { if opname == "branch" { local cond = me._find_int_in(seg, "\"cond\":") local then_id = me._find_int_in(seg, "\"then\":") local else_id = me._find_int_in(seg, "\"else\":") local cval = 0 if cond != null { cval = me._get_map(regs, "" + cond) } if cval != 0 { bb = then_id } else { bb = else_id } scan = block_end moved = 1 break } else { if opname == "ret" { local rv = me._find_int_in(seg, "\"value\":") if rv == null { rv = 0 } return me._get_map(regs, "" + rv) } } } } } } // advance to the end of this instruction object scan = obj_end } // No ret encountered in this block; if control moved, continue with new bb if moved == 1 { continue } // Fallback when ret not found at all in processed blocks return me._extract_first_const_i64(mjson) } return me._extract_first_const_i64(mjson) } }