restore(lang): full lang tree from ff3ef452 (306 files) — compiler, vm, shared, runner, c-abi, etc.\n\n- Restores lang/ directory (files≈306, dirs≈64) as per historical branch with selfhost sources\n- Keeps our recent parser index changes in compiler/* (merged clean by checkout)\n- Unblocks selfhost development and documentation references
This commit is contained in:
8
lang/src/shared/README.md
Normal file
8
lang/src/shared/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
selfhost/shared — Shared boxes
|
||||
|
||||
Responsibilities
|
||||
- Minimal MIR schema/builders and JSON helpers used by compiler and VM.
|
||||
- No execution or parsing side effects.
|
||||
|
||||
Allowed imports
|
||||
- selfhost/shared/* only; no compiler/vm imports.
|
||||
37
lang/src/shared/adapters/map_kv_string_to_array.hako
Normal file
37
lang/src/shared/adapters/map_kv_string_to_array.hako
Normal file
@ -0,0 +1,37 @@
|
||||
// map_kv_string_to_array.hako — Adapter to convert keysS/valuesS string to ArrayBox
|
||||
// Responsibility: Provide split helpers (String -> ArrayBox) for MapBox keys/values
|
||||
|
||||
static box MapKvStringToArrayAdapter {
|
||||
// Split a newline-joined String into an ArrayBox of strings.
|
||||
// Empty or null-like input yields empty ArrayBox.
|
||||
split_lines(s) {
|
||||
local out = new ArrayBox()
|
||||
if s == null { return out }
|
||||
// Simple scan: split by "\n"
|
||||
local i = 0
|
||||
local start = 0
|
||||
local n = s.size()
|
||||
loop(i < n) {
|
||||
if s.substring(i, i+1) == "\n" {
|
||||
out.push(s.substring(start, i))
|
||||
start = i + 1
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
// tail
|
||||
if start <= n { out.push(s.substring(start, n)) }
|
||||
return out
|
||||
}
|
||||
|
||||
// Adapt Map.keysS() -> ArrayBox
|
||||
adapt_keysS(map) {
|
||||
local ks = map.keysS()
|
||||
return me.split_lines(ks)
|
||||
}
|
||||
|
||||
// Adapt Map.valuesS() -> ArrayBox
|
||||
adapt_valuesS(map) {
|
||||
local vs = map.valuesS()
|
||||
return me.split_lines(vs)
|
||||
}
|
||||
}
|
||||
20
lang/src/shared/backend/llvm_backend_box.hako
Normal file
20
lang/src/shared/backend/llvm_backend_box.hako
Normal file
@ -0,0 +1,20 @@
|
||||
// LlvmBackendBox — Hako ABI (stub)
|
||||
// File-handoff only: compile MIR(JSON v0) to object, then link to EXE.
|
||||
// This is a stub for call-site wiring; current implementation is provided by external tools (ny-llvmc).
|
||||
|
||||
static box LlvmBackendBox {
|
||||
// Returns object path on success, or throws (Err) on failure.
|
||||
compile_obj(json_path) {
|
||||
// Stub only (dev): return a deterministic obj path to guide call sites.
|
||||
// Real implementation should shell out to ny-llvmc / harness.
|
||||
if json_path == null { throw "LlvmBackend.compile_obj: json_path is null" }
|
||||
local stem = json_path + ".o" // naive; callers should pass preferred path later
|
||||
return stem
|
||||
}
|
||||
|
||||
// Links an exe. Returns true on success (stub always false to prevent accidental use).
|
||||
link_exe(obj_path, out_path, libs) {
|
||||
if obj_path == null || out_path == null { throw "LlvmBackend.link_exe: path is null" }
|
||||
return false // stub
|
||||
}
|
||||
}
|
||||
121
lang/src/shared/common/box_helpers.hako
Normal file
121
lang/src/shared/common/box_helpers.hako
Normal file
@ -0,0 +1,121 @@
|
||||
// box_helpers.hako — 共通Box操作ヘルパー (Phase 31.2 共通化)
|
||||
// 目的: ArrayBox/MapBox の安全な操作を統一的に提供
|
||||
// 削減: 7ファイル × 2パターン = 14重複 → 1箇所に集約
|
||||
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
|
||||
static box BoxHelpers {
|
||||
// ArrayBox.size/1 の結果unwrap (MapBox-wrapped integer対応)
|
||||
array_len(arr) {
|
||||
if arr == null { return 0 }
|
||||
local size_val = call("ArrayBox.size/1", arr)
|
||||
local repr = "" + size_val
|
||||
if repr.indexOf("MapBox(") == 0 {
|
||||
local inner = call("MapBox.get/2", size_val, "value")
|
||||
if inner != null { return inner }
|
||||
}
|
||||
return size_val
|
||||
}
|
||||
|
||||
// ArrayBox.get/2 の安全呼び出し
|
||||
array_get(arr, idx) {
|
||||
if arr == null { return null }
|
||||
return call("ArrayBox.get/2", arr, idx)
|
||||
}
|
||||
|
||||
// MapBox.get/2 の安全呼び出し
|
||||
map_get(obj, key) {
|
||||
if obj == null { return null }
|
||||
return call("MapBox.get/2", obj, key)
|
||||
}
|
||||
|
||||
// MapBox.set/3 の安全呼び出し(形だけ統一)
|
||||
map_set(obj, key, val) {
|
||||
if obj == null { obj = new MapBox() }
|
||||
call("MapBox.set/3", obj, key, val)
|
||||
return obj
|
||||
}
|
||||
|
||||
// MapBox-wrapped integer の unwrap (汎用版)
|
||||
value_i64(val) {
|
||||
if val == null { return 0 }
|
||||
local repr = "" + val
|
||||
if repr.indexOf("MapBox(") == 0 {
|
||||
local inner = call("MapBox.get/2", val, "value")
|
||||
if inner != null { return inner }
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// MapBox型判定
|
||||
is_map(val) {
|
||||
if val == null { return 0 }
|
||||
local repr = "" + val
|
||||
if repr.indexOf("MapBox(") == 0 { return 1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
// ArrayBox型判定
|
||||
is_array(val) {
|
||||
if val == null { return 0 }
|
||||
local repr = "" + val
|
||||
if repr.indexOf("ArrayBox(") == 0 { return 1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
// Fail-fast helpers (Phase 31.3+)
|
||||
expect_map(val, context) {
|
||||
if val == null {
|
||||
print("[BoxHelpers] expected MapBox for " + context + " but got null")
|
||||
call("MapBox.get/2", val, "__box_helpers_expect_map_null")
|
||||
return val
|
||||
}
|
||||
if me.is_map(val) == 1 { return val }
|
||||
print("[BoxHelpers] dev assert failed: expected MapBox for " + context)
|
||||
call("MapBox.get/2", val, "__box_helpers_expect_map")
|
||||
return val
|
||||
}
|
||||
|
||||
expect_array(val, context) {
|
||||
if val == null {
|
||||
print("[BoxHelpers] expected ArrayBox for " + context + " but got null")
|
||||
call("ArrayBox.get/2", val, 0)
|
||||
return val
|
||||
}
|
||||
if me.is_array(val) == 1 { return val }
|
||||
print("[BoxHelpers] dev assert failed: expected ArrayBox for " + context)
|
||||
call("ArrayBox.get/2", val, 0)
|
||||
return val
|
||||
}
|
||||
|
||||
expect_i64(val, context) {
|
||||
if val == null {
|
||||
print("[BoxHelpers] dev assert failed: expected i64 (non-null) for " + context)
|
||||
call("MapBox.get/2", val, "__box_helpers_expect_i64_null")
|
||||
return 0
|
||||
}
|
||||
local repr = "" + val
|
||||
if repr.indexOf("MapBox(") == 0 {
|
||||
local ty = call("MapBox.get/2", val, "type")
|
||||
if ty != null {
|
||||
local ty_str = "" + ty
|
||||
if ty_str != "i64" && ty_str != "int" && ty_str != "integer" {
|
||||
print("[BoxHelpers] dev assert failed: unexpected type " + ty_str + " in " + context)
|
||||
call("MapBox.get/2", val, "__box_helpers_expect_i64_type")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
local inner = call("MapBox.get/2", val, "value")
|
||||
if inner != null { return StringHelpers.to_i64(inner) }
|
||||
print("[BoxHelpers] dev assert failed: missing value in " + context)
|
||||
call("MapBox.get/2", val, "__box_helpers_expect_i64_value")
|
||||
return 0
|
||||
}
|
||||
if StringHelpers.is_numeric_str("" + val) == 1 { return StringHelpers.to_i64(val) }
|
||||
print("[BoxHelpers] dev assert failed: expected numeric value for " + context)
|
||||
call("MapBox.get/2", val, "__box_helpers_expect_i64_direct")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
static box BoxHelpersStub { main(args) { return 0 } }
|
||||
276
lang/src/shared/common/mini_vm_binop.hako
Normal file
276
lang/src/shared/common/mini_vm_binop.hako
Normal file
@ -0,0 +1,276 @@
|
||||
using "lang/src/vm/boxes/json_cur.hako" as MiniJsonCur
|
||||
using "lang/src/shared/common/mini_vm_scan.hako" as MiniVmScan
|
||||
|
||||
static box MiniVmBinOp {
|
||||
// Minimal: Print(BinaryOp) with operator "+"; supports string+string and int+int
|
||||
try_print_binop_at(json, end, print_pos) {
|
||||
local scan = new MiniVmScan()
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = scan.index_of_from(json, k_bo, print_pos)
|
||||
if bpos <= 0 || bpos >= end { return -1 }
|
||||
// bound BinaryOp object (prefer expression object)
|
||||
local k_expr = "\"expression\":{"
|
||||
local expr_pos = scan.index_of_from(json, k_expr, print_pos)
|
||||
local obj_start = -1
|
||||
if expr_pos > 0 && expr_pos < end {
|
||||
obj_start = scan.index_of_from(json, "{", expr_pos)
|
||||
} else {
|
||||
obj_start = scan.index_of_from(json, "{", bpos)
|
||||
}
|
||||
local obj_end = scan.find_balanced_object_end(json, obj_start)
|
||||
if obj_start <= 0 || obj_end <= 0 || obj_end > end { return -1 }
|
||||
// operator must be '+'
|
||||
local k_op = "\"operator\":\"+\""
|
||||
local opos = scan.index_of_from(json, k_op, bpos)
|
||||
if opos <= 0 || opos >= obj_end { return -1 }
|
||||
|
||||
// string + string fast-path
|
||||
local cur = new MiniJson()
|
||||
local k_left_lit = "\"left\":{\"kind\":\"Literal\""
|
||||
local lhdr = scan.index_of_from(json, k_left_lit, opos)
|
||||
if lhdr > 0 && lhdr < obj_end {
|
||||
local k_sval = "\"value\":\""
|
||||
local lvp = scan.index_of_from(json, k_sval, lhdr)
|
||||
if lvp > 0 && lvp < obj_end {
|
||||
local li = lvp + k_sval.size()
|
||||
local lval = cur.read_quoted_from(json, li)
|
||||
if lval {
|
||||
local k_right_lit = "\"right\":{\"kind\":\"Literal\""
|
||||
local rhdr = scan.index_of_from(json, k_right_lit, li + lval.size())
|
||||
if rhdr > 0 && rhdr < obj_end {
|
||||
local rvp = scan.index_of_from(json, k_sval, rhdr)
|
||||
if rvp > 0 && rvp < obj_end {
|
||||
local ri = rvp + k_sval.size()
|
||||
local rval = cur.read_quoted_from(json, ri)
|
||||
if rval { print(lval + rval) return ri + rval.size() + 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// int + int typed pattern
|
||||
local k_l = "\"left\":{\"kind\":\"Literal\""
|
||||
local lpos = scan.index_of_from(json, k_l, opos)
|
||||
if lpos <= 0 || lpos >= obj_end { return -1 }
|
||||
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local li2 = scan.index_of_from(json, k_lint, opos)
|
||||
if li2 <= 0 || li2 >= obj_end { return -1 }
|
||||
local ldigits = scan.read_digits(json, li2 + k_lint.size())
|
||||
if ldigits == "" { return -1 }
|
||||
local k_r = "\"right\":{\"kind\":\"Literal\""
|
||||
local rpos = scan.index_of_from(json, k_r, lpos)
|
||||
if rpos <= 0 || rpos >= obj_end { return -1 }
|
||||
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local ri2 = scan.index_of_from(json, k_rint, lpos)
|
||||
if ri2 <= 0 || ri2 >= obj_end { return -1 }
|
||||
local rdigits = scan.read_digits(json, ri2 + k_rint.size())
|
||||
if rdigits == "" { return -1 }
|
||||
local ai = scan._str_to_int(ldigits)
|
||||
local bi = scan._str_to_int(rdigits)
|
||||
print(scan._int_to_str(ai + bi))
|
||||
return obj_end + 1
|
||||
}
|
||||
|
||||
// Greedy disabled (kept for parity)
|
||||
try_print_binop_int_greedy(json, end, print_pos) { return -1 }
|
||||
|
||||
// Fallback: within the current Print's expression BinaryOp object, scan for two numeric values and sum
|
||||
try_print_binop_sum_any(json, end, print_pos) {
|
||||
local scan = new MiniVmScan()
|
||||
local k_expr = "\"expression\":{"
|
||||
local expr_pos = scan.index_of_from(json, k_expr, print_pos)
|
||||
// If expression object cannot be bounded, fall back to typed-direct pattern within the current slice
|
||||
if expr_pos <= 0 || expr_pos >= end {
|
||||
// bound coarse slice to current Print by next Print marker
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
local next_p = scan.index_of_from(json, k_print, print_pos + 1)
|
||||
local slice_end = end
|
||||
if next_p > 0 { slice_end = next_p }
|
||||
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
// only if BinaryOp is present in this slice
|
||||
if scan.index_of_from(json, "\"kind\":\"BinaryOp\"", print_pos) > 0 {
|
||||
local lp = scan.index_of_from(json, k_lint, print_pos)
|
||||
if lp > 0 { if lp < slice_end {
|
||||
local ld = scan.read_digits(json, lp + k_lint.size())
|
||||
if ld != "" {
|
||||
local rp = scan.index_of_from(json, k_rint, lp + k_lint.size())
|
||||
if rp > 0 { if rp < slice_end {
|
||||
local rd = scan.read_digits(json, rp + k_rint.size())
|
||||
if rd != "" { print(scan._int_to_str(scan._str_to_int(ld) + scan._str_to_int(rd))) return 1 }
|
||||
}}
|
||||
}
|
||||
}}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
local obj_start = scan.index_of_from(json, "{", expr_pos)
|
||||
if obj_start <= 0 || obj_start >= end { return -1 }
|
||||
local obj_end = scan.find_balanced_object_end(json, obj_start)
|
||||
if obj_end <= 0 || obj_end > end {
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
local next_p = scan.index_of_from(json, k_print, print_pos + 1)
|
||||
local obj_end2 = end
|
||||
if next_p > 0 && next_p <= end { obj_end2 = next_p }
|
||||
// typed-direct fallback in bounded region
|
||||
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local lp = scan.index_of_from(json, k_lint, obj_start)
|
||||
if lp > 0 { if lp < obj_end2 {
|
||||
local ld = scan.read_digits(json, lp + k_lint.size())
|
||||
if ld != "" {
|
||||
local rp = scan.index_of_from(json, k_rint, lp + k_lint.size())
|
||||
if rp > 0 { if rp < obj_end2 {
|
||||
local rd = scan.read_digits(json, rp + k_rint.size())
|
||||
if rd != "" { print(scan._int_to_str(scan._str_to_int(ld) + scan._str_to_int(rd))) return 1 }
|
||||
}}
|
||||
}
|
||||
}}
|
||||
return -1
|
||||
}
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = scan.index_of_from(json, k_bo, obj_start)
|
||||
if bpos <= 0 || bpos >= obj_end {
|
||||
// typed-direct fallback (within bounds window)
|
||||
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local lp = scan.index_of_from(json, k_lint, obj_start)
|
||||
if lp > 0 { if lp < obj_end {
|
||||
local ld = scan.read_digits(json, lp + k_lint.size())
|
||||
if ld != "" {
|
||||
local rp = scan.index_of_from(json, k_rint, lp + k_lint.size())
|
||||
if rp > 0 { if rp < obj_end {
|
||||
local rd = scan.read_digits(json, rp + k_rint.size())
|
||||
if rd != "" { print(scan._int_to_str(scan._str_to_int(ld) + scan._str_to_int(rd))) return 1 }
|
||||
}}
|
||||
}
|
||||
}}
|
||||
return -1
|
||||
}
|
||||
local k_plus = "\"operator\":\"+\""
|
||||
local opos = scan.index_of_from(json, k_plus, bpos)
|
||||
if opos <= 0 || opos >= obj_end {
|
||||
// typed-direct fallback
|
||||
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local lp = scan.index_of_from(json, k_lint, obj_start)
|
||||
if lp > 0 { if lp < obj_end {
|
||||
local ld = scan.read_digits(json, lp + k_lint.size())
|
||||
if ld != "" {
|
||||
local rp = scan.index_of_from(json, k_rint, lp + k_lint.size())
|
||||
if rp > 0 { if rp < obj_end {
|
||||
local rd = scan.read_digits(json, rp + k_rint.size())
|
||||
if rd != "" { print(scan._int_to_str(scan._str_to_int(ld) + scan._str_to_int(rd))) return 1 }
|
||||
}}
|
||||
}
|
||||
}}
|
||||
return -1
|
||||
}
|
||||
local nums = []
|
||||
local i = obj_start
|
||||
loop (i < obj_end) {
|
||||
if call("String.substring/2", json, i, i+1) == "\"" {
|
||||
local j = scan.index_of_from(json, "\"", i+1)
|
||||
if j < 0 || j >= obj_end { break }
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
local d = scan.read_digits(json, i)
|
||||
if d { nums.push(d) i = i + d.size() continue }
|
||||
i = i + 1
|
||||
}
|
||||
local nsz = nums.size()
|
||||
if nsz < 2 { return -1 }
|
||||
local a = scan._str_to_int(nums.get(nsz-2))
|
||||
local b = scan._str_to_int(nums.get(nsz-1))
|
||||
print(scan._int_to_str(a + b))
|
||||
return obj_end + 1
|
||||
}
|
||||
|
||||
// Deterministic: within Print.expression BinaryOp('+'), pick two successive 'value' fields and sum
|
||||
try_print_binop_sum_expr_values(json, end, print_pos) {
|
||||
local scan = new MiniVmScan()
|
||||
local cur = new MiniJson()
|
||||
local k_expr = "\"expression\":{"
|
||||
local expr_pos = scan.index_of_from(json, k_expr, print_pos)
|
||||
if expr_pos <= 0 || expr_pos >= end { return -1 }
|
||||
local obj_start = scan.index_of_from(json, "{", expr_pos)
|
||||
if obj_start <= 0 || obj_start >= end { return -1 }
|
||||
local obj_end = scan.find_balanced_object_end(json, obj_start)
|
||||
if obj_end <= 0 || obj_end > end { return -1 }
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = scan.index_of_from(json, k_bo, obj_start)
|
||||
if bpos <= 0 || bpos >= obj_end { return -1 }
|
||||
local k_plus = "\"operator\":\"+\""
|
||||
local opos = scan.index_of_from(json, k_plus, bpos)
|
||||
if opos <= 0 || opos >= obj_end { return -1 }
|
||||
local k_v = "\"value\":"
|
||||
local found = 0
|
||||
local a = 0
|
||||
local pos = scan.index_of_from(json, k_v, obj_start)
|
||||
loop (pos > 0 && pos < obj_end) {
|
||||
local di = cur.read_digits_from(json, pos + k_v.size())
|
||||
if di != "" {
|
||||
if found == 0 { a = scan._str_to_int(di) found = 1 } else {
|
||||
local b = scan._str_to_int(di)
|
||||
print(scan._int_to_str(a + b))
|
||||
return obj_end + 1
|
||||
}
|
||||
}
|
||||
pos = scan.index_of_from(json, k_v, pos + k_v.size())
|
||||
if pos <= 0 || pos >= obj_end { break }
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Simpler: after operator '+', scan two successive 'value' fields and sum
|
||||
try_print_binop_sum_after_bop(json) {
|
||||
local scan = new MiniVmScan()
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = json.indexOf(k_bo)
|
||||
if bpos < 0 { return -1 }
|
||||
local k_plus = "\"operator\":\"+\""
|
||||
local opos = scan.index_of_from(json, k_plus, bpos)
|
||||
if opos < 0 { return -1 }
|
||||
local k_v = "\"value\":"
|
||||
local p = opos
|
||||
p = scan.index_of_from(json, k_v, p)
|
||||
if p < 0 { return -1 }
|
||||
p = scan.index_of_from(json, k_v, p + k_v.size())
|
||||
if p < 0 { return -1 }
|
||||
local end1 = scan.index_of_from(json, "}", p)
|
||||
if end1 < 0 { return -1 }
|
||||
local d1 = scan.read_digits(json, p + k_v.size())
|
||||
if d1 == "" { return -1 }
|
||||
p = scan.index_of_from(json, k_v, end1)
|
||||
if p < 0 { return -1 }
|
||||
p = scan.index_of_from(json, k_v, p + k_v.size())
|
||||
if p < 0 { return -1 }
|
||||
local end2 = scan.index_of_from(json, "}", p)
|
||||
if end2 < 0 { return -1 }
|
||||
local d2 = scan.read_digits(json, p + k_v.size())
|
||||
if d2 == "" { return -1 }
|
||||
local ai = scan._str_to_int(d1)
|
||||
local bi = scan._str_to_int(d2)
|
||||
print(scan._int_to_str(ai + bi))
|
||||
return 0
|
||||
}
|
||||
|
||||
// Fallback: find first BinaryOp and return sum of two numeric values as string
|
||||
parse_first_binop_sum(json) {
|
||||
local scan = new MiniVmScan()
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = json.indexOf(k_bo)
|
||||
if bpos < 0 { return "" }
|
||||
local k_typed = "\"type\":\"int\",\"value\":"
|
||||
local p1 = scan.index_of_from(json, k_typed, bpos)
|
||||
if p1 < 0 { return "" }
|
||||
local d1 = scan.read_digits(json, p1 + k_typed.size())
|
||||
if d1 == "" { return "" }
|
||||
local p2 = scan.index_of_from(json, k_typed, p1 + k_typed.size())
|
||||
if p2 < 0 { return "" }
|
||||
local d2 = scan.read_digits(json, p2 + k_typed.size())
|
||||
if d2 == "" { return "" }
|
||||
return scan._int_to_str(scan._str_to_int(d1) + scan._str_to_int(d2))
|
||||
}
|
||||
}
|
||||
49
lang/src/shared/common/mini_vm_compare.hako
Normal file
49
lang/src/shared/common/mini_vm_compare.hako
Normal file
@ -0,0 +1,49 @@
|
||||
using "lang/src/shared/common/mini_vm_scan.hako" as MiniVmScan
|
||||
|
||||
static box MiniVmCompare {
|
||||
// Compare(lhs int, rhs int) minimal: prints 0/1 and returns next pos or -1
|
||||
try_print_compare_at(json, end, print_pos) {
|
||||
local scan = new MiniVmScan()
|
||||
local k_cp = "\"kind\":\"Compare\""
|
||||
local cpos = scan.indexOf(k_cp)
|
||||
if cpos < 0 { cpos = scan.index_of_from(json, k_cp, print_pos) }
|
||||
if cpos <= 0 || cpos >= end { return -1 }
|
||||
local k_op = "\"operation\":\""
|
||||
local opos = scan.index_of_from(json, k_op, cpos)
|
||||
if opos <= 0 || opos >= end { return -1 }
|
||||
local oi = opos + k_op.size()
|
||||
local oj = scan.index_of_from(json, "\"", oi)
|
||||
if oj <= 0 || oj > end { return -1 }
|
||||
local op = json.substring(oi, oj)
|
||||
// lhs value
|
||||
local k_lhs = "\"lhs\":{\"kind\":\"Literal\""
|
||||
local hl = scan.index_of_from(json, k_lhs, oj)
|
||||
if hl <= 0 || hl >= end { return -1 }
|
||||
local k_v = "\"value\":"
|
||||
local hv = scan.index_of_from(json, k_v, hl)
|
||||
if hv <= 0 || hv >= end { return -1 }
|
||||
local a = scan.read_digits(json, hv + k_v.size())
|
||||
// rhs value
|
||||
local k_rhs = "\"rhs\":{\"kind\":\"Literal\""
|
||||
local hr = scan.index_of_from(json, k_rhs, hl)
|
||||
if hr <= 0 || hr >= end { return -1 }
|
||||
local rv = scan.index_of_from(json, k_v, hr)
|
||||
if rv <= 0 || rv >= end { return -1 }
|
||||
local b = scan.read_digits(json, rv + k_v.size())
|
||||
if !a || !b { return -1 }
|
||||
local ai = scan._str_to_int(a)
|
||||
local bi = scan._str_to_int(b)
|
||||
local res = match op {
|
||||
"<" => { if ai < bi { 1 } else { 0 } }
|
||||
"==" => { if ai == bi { 1 } else { 0 } }
|
||||
"<=" => { if ai <= bi { 1 } else { 0 } }
|
||||
">" => { if ai > bi { 1 } else { 0 } }
|
||||
">=" => { if ai >= bi { 1 } else { 0 } }
|
||||
"!=" => { if ai != bi { 1 } else { 0 } }
|
||||
_ => 0
|
||||
}
|
||||
print(res)
|
||||
// advance after rhs object (coarsely)
|
||||
return rv + 1
|
||||
}
|
||||
}
|
||||
79
lang/src/shared/common/mini_vm_scan.hako
Normal file
79
lang/src/shared/common/mini_vm_scan.hako
Normal file
@ -0,0 +1,79 @@
|
||||
// Mini-VM scanning and numeric helpers
|
||||
// Delegation Policy: all scanning primitives route to JsonCursorBox to avoid divergence.
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
|
||||
static box MiniVmScan {
|
||||
// helper: find needle from position pos (escape-aware where needed)
|
||||
index_of_from(hay, needle, pos) { return JsonCursorBox.index_of_from(hay, needle, pos) }
|
||||
|
||||
// helper: find balanced bracket range [ ... ] starting at idx (points to '[')
|
||||
find_balanced_array_end(json, idx) { return JsonCursorBox.seek_array_end(json, idx) }
|
||||
|
||||
// helper: find balanced object range { ... } starting at idx (points to '{')
|
||||
find_balanced_object_end(json, idx) { return JsonCursorBox.seek_obj_end(json, idx) }
|
||||
|
||||
_str_to_int(s) { return StringHelpers.to_i64(s) }
|
||||
_int_to_str(n) { return StringHelpers.int_to_str(n) }
|
||||
read_digits(json, pos) { return StringHelpers.read_digits(json, pos) }
|
||||
|
||||
// Linear pass: sum all numbers outside of quotes
|
||||
sum_numbers_no_quotes(json) {
|
||||
@i = 0
|
||||
@n = json.size()
|
||||
@total = 0
|
||||
loop (i < n) {
|
||||
@ch = call("String.substring/2", json, i, i+1)
|
||||
if ch == "\"" {
|
||||
@j = me.index_of_from(json, "\"", i+1)
|
||||
if j < 0 { break }
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
@d = me.read_digits(json, i)
|
||||
if d { total = total + me._str_to_int(d) i = i + d.size() continue }
|
||||
i = i + 1
|
||||
}
|
||||
return me._int_to_str(total)
|
||||
}
|
||||
|
||||
// Naive: sum all digit runs anywhere
|
||||
sum_all_digits_naive(json) {
|
||||
@i = 0
|
||||
@n = json.size()
|
||||
@total = 0
|
||||
loop (i < n) {
|
||||
@d = me.read_digits(json, i)
|
||||
if d { total = total + me._str_to_int(d) i = i + d.size() continue }
|
||||
i = i + 1
|
||||
}
|
||||
return me._int_to_str(total)
|
||||
}
|
||||
|
||||
// Sum first two integers outside quotes; returns string or empty
|
||||
sum_first_two_numbers(json) {
|
||||
@i = 0
|
||||
@n = json.size()
|
||||
@total = 0
|
||||
local found = 0
|
||||
loop (i < n) {
|
||||
@ch = call("String.substring/2", json, i, i+1)
|
||||
if ch == "\"" {
|
||||
@j = me.index_of_from(json, "\"", i+1)
|
||||
if j < 0 { break }
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
@d = me.read_digits(json, i)
|
||||
if d {
|
||||
total = total + me._str_to_int(d)
|
||||
found = found + 1
|
||||
i = i + d.size()
|
||||
if found >= 2 { return me._int_to_str(total) }
|
||||
continue
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
39
lang/src/shared/common/modules_inspect_box.hako
Normal file
39
lang/src/shared/common/modules_inspect_box.hako
Normal file
@ -0,0 +1,39 @@
|
||||
// modules_inspect_box.hako — ModulesInspectBox
|
||||
// Responsibility: Helpers for Dir-as-NS module inspection from Nyash code.
|
||||
// Non‑IO, formatting-only utilities. Caller provides inputs (e.g., lists or JSON).
|
||||
|
||||
static box ModulesInspectBox {
|
||||
// Convert apps-relative path to namespace (Dir-as-NS v2.2 rules)
|
||||
path_to_namespace(path) {
|
||||
if path == null { return "" }
|
||||
local s = "" + path
|
||||
// Strip leading ./
|
||||
if s.size() >= 2 && s.substring(0,2) == "./" { s = s.substring(2, s.size()) }
|
||||
// Remove leading apps/
|
||||
local pref = "apps/"
|
||||
if s.size() >= pref.size() && s.substring(0, pref.size()) == pref { s = s.substring(pref.size(), s.size()) }
|
||||
// Replace '-' with '.' in directory parts only
|
||||
local out = ""
|
||||
local i = 0
|
||||
loop(i < s.size()) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "/" { out = out + "." i = i + 1 continue }
|
||||
if ch == "-" { out = out + "." i = i + 1 continue }
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
}
|
||||
// Drop .hako and optional _box suffix
|
||||
if out.size() >= 5 && out.substring(out.size()-5, out.size()) == ".hako" {
|
||||
out = out.substring(0, out.size()-5)
|
||||
}
|
||||
if out.size() >= 4 && out.substring(out.size()-4, out.size()) == "_box" {
|
||||
out = out.substring(0, out.size()-4)
|
||||
}
|
||||
return out
|
||||
}
|
||||
// Pretty single line
|
||||
pretty_line(tag, ns, path) { return "[" + tag + "] " + ns + " → " + path }
|
||||
}
|
||||
|
||||
static box ModulesInspectMain { main(args){ return 0 } }
|
||||
|
||||
174
lang/src/shared/common/string_helpers.hako
Normal file
174
lang/src/shared/common/string_helpers.hako
Normal file
@ -0,0 +1,174 @@
|
||||
// string_helpers.hako — StringHelpers (common, pure helpers)
|
||||
// Responsibility: numeric/string conversions and JSON quoting for selfhost tools.
|
||||
// Non-responsibility: JSON scanning beyond local character processing; use CfgNavigatorBox/StringScanBox for navigation.
|
||||
|
||||
static box StringHelpers {
|
||||
// Convert a numeric or numeric-like string to its decimal representation.
|
||||
int_to_str(n) {
|
||||
local v = me.to_i64(n)
|
||||
if v == 0 { return "0" }
|
||||
if v < 0 { return "-" + me.int_to_str(0 - v) }
|
||||
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
|
||||
}
|
||||
|
||||
// Parse integer from number or numeric-like string (leading '-' allowed; stops at first non-digit).
|
||||
to_i64(x) {
|
||||
local s = "" + x
|
||||
local i = 0
|
||||
local neg = 0
|
||||
if s.substring(0,1) == "-" { neg = 1 i = 1 }
|
||||
local n = s.size()
|
||||
if i >= n { return 0 }
|
||||
local acc = 0
|
||||
loop (i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch < "0" || ch > "9" { break }
|
||||
local ds = "0123456789"
|
||||
local dpos = ds.indexOf(ch)
|
||||
if dpos < 0 { break }
|
||||
acc = acc * 10 + dpos
|
||||
i = i + 1
|
||||
}
|
||||
if neg == 1 { return 0 - acc }
|
||||
return acc
|
||||
}
|
||||
|
||||
// Quote a string for JSON (escape backslash, quote, and control chars) and wrap with quotes
|
||||
json_quote(s) {
|
||||
if s == null { return "\"\"" }
|
||||
local out = ""
|
||||
local i = 0
|
||||
local n = s.size()
|
||||
loop (i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "\\" { out = out + "\\\\" }
|
||||
else { if ch == "\"" { out = out + "\\\"" } else {
|
||||
if ch == "\n" { out = out + "\\n" } else {
|
||||
if ch == "\r" { out = out + "\\r" } else {
|
||||
if ch == "\t" { out = out + "\\t" } else { out = out + ch }
|
||||
}
|
||||
}
|
||||
}}
|
||||
i = i + 1
|
||||
}
|
||||
return "\"" + out + "\""
|
||||
}
|
||||
|
||||
// Check if string is numeric-like (optional leading '-', then digits).
|
||||
is_numeric_str(s) {
|
||||
if s == null { return 0 }
|
||||
local n = s.size()
|
||||
if n == 0 { return 0 }
|
||||
local i = 0
|
||||
if s.substring(0,1) == "-" { if n == 1 { return 0 } i = 1 }
|
||||
loop(i < n) { local ch = s.substring(i, i+1) if ch < "0" || ch > "9" { return 0 } i = i + 1 }
|
||||
return 1
|
||||
}
|
||||
|
||||
// Read consecutive digits starting at pos (no sign handling here; keep semantics simple)
|
||||
read_digits(text, pos) {
|
||||
local out = ""
|
||||
loop (true) {
|
||||
local ch = text.substring(pos, pos+1)
|
||||
if ch == "" { break }
|
||||
if ch >= "0" && ch <= "9" { out = out + ch pos = pos + 1 } else { break }
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// 🆕 Extended String Utilities (added for parser/compiler unification)
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
// Character predicates
|
||||
is_digit(ch) { return ch >= "0" && ch <= "9" }
|
||||
is_alpha(ch) { return (ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z") || ch == "_" }
|
||||
is_space(ch) { return ch == " " || ch == "\t" || ch == "\n" || ch == "\r" }
|
||||
|
||||
// Pattern matching
|
||||
starts_with(src, i, pat) {
|
||||
local n = src.size()
|
||||
local m = pat.size()
|
||||
if i + m > n { return 0 }
|
||||
local k = 0
|
||||
loop(k < m) {
|
||||
if src.substring(i + k, i + k + 1) != pat.substring(k, k + 1) { return 0 }
|
||||
k = k + 1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// Keyword match with word boundary (next char not [A-Za-z0-9_])
|
||||
starts_with_kw(src, i, kw) {
|
||||
if me.starts_with(src, i, kw) == 0 { return 0 }
|
||||
local n = src.size()
|
||||
local j = i + kw.size()
|
||||
if j >= n { return 1 }
|
||||
local ch = src.substring(j, j+1)
|
||||
if me.is_alpha(ch) || me.is_digit(ch) { return 0 }
|
||||
return 1
|
||||
}
|
||||
|
||||
// String search
|
||||
index_of(src, i, pat) {
|
||||
local n = src.size()
|
||||
local m = pat.size()
|
||||
if m == 0 { return i }
|
||||
local j = i
|
||||
loop(j + m <= n) {
|
||||
if me.starts_with(src, j, pat) { return j }
|
||||
j = j + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Trim spaces and tabs (with optional semicolon at end)
|
||||
trim(s) {
|
||||
local i = 0
|
||||
local n = s.size()
|
||||
loop(i < n && (s.substring(i,i+1) == " " || s.substring(i,i+1) == "\t")) { i = i + 1 }
|
||||
local j = n
|
||||
loop(j > i && (s.substring(j-1,j) == " " || s.substring(j-1,j) == "\t" || s.substring(j-1,j) == ";")) { j = j - 1 }
|
||||
return s.substring(i, j)
|
||||
}
|
||||
|
||||
// Skip whitespace from position i
|
||||
skip_ws(src, i) {
|
||||
if src == null { return i }
|
||||
local n = src.size()
|
||||
local cont = 1
|
||||
local guard = 0
|
||||
local max = 100000
|
||||
loop(cont == 1) {
|
||||
if guard > max { return i } else { guard = guard + 1 }
|
||||
if i < n {
|
||||
if me.is_space(src.substring(i, i+1)) { i = i + 1 } else { cont = 0 }
|
||||
} else { cont = 0 }
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Find last occurrence of pattern in string (backward search)
|
||||
last_index_of(src, pat) {
|
||||
if src == null { return -1 }
|
||||
if pat == null { return -1 }
|
||||
local n = src.size()
|
||||
local m = pat.size()
|
||||
if m == 0 { return n }
|
||||
if m > n { return -1 }
|
||||
local i = n - m
|
||||
loop(i >= 0) {
|
||||
if me.starts_with(src, i, pat) { return i }
|
||||
i = i - 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
23
lang/src/shared/common/string_ops.hako
Normal file
23
lang/src/shared/common/string_ops.hako
Normal file
@ -0,0 +1,23 @@
|
||||
// string_ops.hako — Common string utility functions
|
||||
// Responsibility: Basic string operations used across the codebase
|
||||
// Non-goals: JSON parsing, complex text processing
|
||||
|
||||
static box StringOps {
|
||||
// Find substring starting from position
|
||||
// Returns: index of first occurrence at/after pos, or -1 if not found
|
||||
index_of_from(text, needle, pos) {
|
||||
if text == null { return -1 }
|
||||
if pos < 0 { pos = 0 }
|
||||
local n = text.size()
|
||||
if pos >= n { return -1 }
|
||||
local m = needle.size()
|
||||
if m <= 0 { return pos }
|
||||
local i = pos
|
||||
local limit = n - m
|
||||
loop (i <= limit) {
|
||||
if text.substring(i, i + m) == needle { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
32
lang/src/shared/hako_module.toml
Normal file
32
lang/src/shared/hako_module.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[module]
|
||||
name = "selfhost.shared"
|
||||
version = "1.0.0"
|
||||
|
||||
[exports]
|
||||
# JSON adapter facade and common helpers
|
||||
json_adapter = "json_adapter.hako"
|
||||
common.mini_vm_scan = "common/mini_vm_scan.hako"
|
||||
common.mini_vm_binop = "common/mini_vm_binop.hako"
|
||||
common.mini_vm_compare = "common/mini_vm_compare.hako"
|
||||
common.string_helpers = "common/string_helpers.hako"
|
||||
common.string_ops = "common/string_ops.hako"
|
||||
common.box_helpers = "common/box_helpers.hako"
|
||||
|
||||
# JSON tooling
|
||||
json.mir_builder_min = "json/mir_builder_min.hako"
|
||||
json.mir_v1_adapter = "json/mir_v1_adapter.hako"
|
||||
json.core.json_cursor = "json/json_cursor.hako"
|
||||
json.utils.json_utils = "json/json_utils.hako"
|
||||
|
||||
# MIR helpers (exported as stable module names)
|
||||
mir.schema = "mir/mir_schema_box.hako"
|
||||
mir.builder = "mir/block_builder_box.hako"
|
||||
mir.io = "mir/mir_io_box.hako"
|
||||
mir.json_emit = "mir/json_emit_box.hako"
|
||||
|
||||
[private]
|
||||
# Internal builders kept private for now
|
||||
# json/mir_builder2.hako, json/json_inst_encode_box.hako
|
||||
|
||||
[dependencies]
|
||||
"selfhost.vm" = "^1.0.0"
|
||||
21
lang/src/shared/host_bridge/host_bridge_box.hako
Normal file
21
lang/src/shared/host_bridge/host_bridge_box.hako
Normal file
@ -0,0 +1,21 @@
|
||||
// host_bridge_box.hako — HostBridgeBox (Phase A, Hako-side thin adapter)
|
||||
// Responsibility: Provide minimal API to construct/call host boxes for VM backend.
|
||||
// Note: Phase A supports only a small subset (FileBox). Phase B wires full Hako ABI.
|
||||
|
||||
static box HostBridgeBox {
|
||||
// Create a box by name with args (Phase B: extern → Rust HostBridge)
|
||||
// Route: hostbridge.box_new(name, args)
|
||||
box_new(name, args) {
|
||||
if args == null { args = new ArrayBox() }
|
||||
return hostbridge.box_new(name, args)
|
||||
}
|
||||
|
||||
// Call a method by name with Array args (Phase B: extern → Rust HostBridge)
|
||||
// Route: hostbridge.box_call(receiver, method, args) → lowered to Extern("hostbridge.box_call")
|
||||
box_call(inst, method, args) {
|
||||
if inst == null { return null }
|
||||
if args == null { args = new ArrayBox() }
|
||||
return hostbridge.box_call(inst, method, args)
|
||||
}
|
||||
}
|
||||
|
||||
105
lang/src/shared/json/core/json_scan.hako
Normal file
105
lang/src/shared/json/core/json_scan.hako
Normal file
@ -0,0 +1,105 @@
|
||||
// json_scan.hako — JsonScanBox
|
||||
// Escape-aware JSON structure scanning helpers.
|
||||
|
||||
using "lang/src/shared/json/core/string_scan.hako" as StringScanBox
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
|
||||
static box JsonScanBox {
|
||||
// minimal string→int (delegate to shared helper)
|
||||
_str_to_int(s) { return StringHelpers.to_i64(s) }
|
||||
// Seek the end index (inclusive) of an object starting at `start` (must be '{').
|
||||
seek_obj_end(text, start) {
|
||||
if text == null { return -1 }
|
||||
if start < 0 || start >= text.size() { return -1 }
|
||||
if call("String.substring/2", text, start, start+1) != "{" { return -1 }
|
||||
local n = text.size()
|
||||
local depth = 0
|
||||
local i = start
|
||||
local in_str = 0
|
||||
loop (i < n) {
|
||||
local ch = StringScanBox.read_char(text, i)
|
||||
if in_str == 1 {
|
||||
if ch == "\\" {
|
||||
i = i + 2
|
||||
continue
|
||||
}
|
||||
if ch == "\"" {
|
||||
in_str = 0
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
if ch == "\"" {
|
||||
local j = StringScanBox.find_quote(text, i+1)
|
||||
if j < 0 { return -1 }
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
if ch == "{" {
|
||||
depth = depth + 1
|
||||
} else if ch == "}" {
|
||||
depth = depth - 1
|
||||
if depth == 0 { return i }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Seek the end index (inclusive) of an array starting at `start` (must be '[').
|
||||
seek_array_end(text, start) {
|
||||
if text == null { return -1 }
|
||||
// Normalize start to integer (defensive against stringified input)
|
||||
local start_s = "" + start
|
||||
local start_i = me._str_to_int(start_s)
|
||||
local n = text.size()
|
||||
if start_i < 0 || start_i >= n { return -1 }
|
||||
local first_ch = text.substring(start_i, start_i+1)
|
||||
if first_ch != "[" { return -1 }
|
||||
// main loop (escape-aware)
|
||||
local depth = 0
|
||||
local i = start_i
|
||||
local in_str = 0
|
||||
loop (i < n) {
|
||||
local ch = StringScanBox.read_char(text, i)
|
||||
if in_str == 1 {
|
||||
if ch == "\\" {
|
||||
i = i + 2
|
||||
continue
|
||||
}
|
||||
if ch == "\"" {
|
||||
in_str = 0
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
if ch == "\"" {
|
||||
local j = StringScanBox.find_quote(text, i+1)
|
||||
if j < 0 { return -1 }
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
if ch == "[" {
|
||||
depth = depth + 1
|
||||
} else if ch == "]" {
|
||||
depth = depth - 1
|
||||
if depth == 0 { return i }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Search for either plain or escaped key pattern starting at `pos`.
|
||||
find_key_dual(text, plain, escaped, pos) {
|
||||
if text == null { return -1 }
|
||||
if pos < 0 { pos = 0 }
|
||||
local p = StringScanBox.index_of_from(text, plain, pos)
|
||||
if p >= 0 { return p }
|
||||
return StringScanBox.index_of_from(text, escaped, pos)
|
||||
}
|
||||
}
|
||||
59
lang/src/shared/json/core/string_scan.hako
Normal file
59
lang/src/shared/json/core/string_scan.hako
Normal file
@ -0,0 +1,59 @@
|
||||
// string_scan.hako — StringScanBox
|
||||
// Escape-aware string scanning helpers for JSON/text processing.
|
||||
|
||||
using "lang/src/shared/common/string_ops.hako" as StringOps
|
||||
|
||||
static box StringScanBox {
|
||||
// Return the single-character string at index i (empty if out of bounds)
|
||||
read_char(text, i) {
|
||||
if text == null { return "" }
|
||||
if i < 0 { return "" }
|
||||
local n = text.size()
|
||||
if i >= n { return "" }
|
||||
return text.substring(i, i+1)
|
||||
}
|
||||
// Find next unescaped double quote starting at or after pos
|
||||
find_quote(text, pos) {
|
||||
return me.find_unescaped(text, "\"", pos)
|
||||
}
|
||||
// Find substring starting from position (simple, non-escape-aware)
|
||||
index_of_from(text, needle, pos) {
|
||||
return StringOps.index_of_from(text, needle, pos)
|
||||
}
|
||||
// Find next occurrence of character `ch` in `text` at or after `pos`,
|
||||
// ignoring escaped occurrences (preceded by a backslash).
|
||||
find_unescaped(text, ch, pos) {
|
||||
if text == null { return -1 }
|
||||
if pos < 0 { pos = 0 }
|
||||
local n = text.size()
|
||||
local i = pos
|
||||
loop (i < n) {
|
||||
local c = me.read_char(text, i)
|
||||
if c == "\\" {
|
||||
// skip escape sequence
|
||||
i = i + 2
|
||||
continue
|
||||
}
|
||||
if c == ch { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Given a starting index at a double quote, find the closing quote index
|
||||
// respecting escape sequences; returns -1 if unterminated.
|
||||
scan_string_end(text, start) {
|
||||
if text == null { return -1 }
|
||||
if start < 0 || start >= text.size() { return -1 }
|
||||
if text.substring(start, start+1) != "\"" { return -1 }
|
||||
local i = start + 1
|
||||
local n = text.size()
|
||||
loop (i < n) {
|
||||
local c = me.read_char(text, i)
|
||||
if c == "\\" { i = i + 2 continue }
|
||||
if c == "\"" { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
12
lang/src/shared/json/json_canonical_box.hako
Normal file
12
lang/src/shared/json/json_canonical_box.hako
Normal file
@ -0,0 +1,12 @@
|
||||
// json_canonical_box.hako — JsonCanonicalBox (Phase 20.5 Gate A unification)
|
||||
// Responsibility: Provide a single entry to canonicalize JSON strings.
|
||||
// Current behavior: identity (no-op) until Extern anchor bridging is wired.
|
||||
// Future: call host anchor `nyash_json_canonicalize_h` via Extern/HostBridge to obtain canonical form.
|
||||
|
||||
static box JsonCanonicalBox {
|
||||
canonicalize(json) {
|
||||
// Phase-A: identity(host bridge未接続の間は常にno-op)
|
||||
// 期待動作: 文字列正規化は将来のExternに委譲。現状はそのまま返す。
|
||||
return "" + json
|
||||
}
|
||||
}
|
||||
44
lang/src/shared/json/json_cursor.hako
Normal file
44
lang/src/shared/json/json_cursor.hako
Normal file
@ -0,0 +1,44 @@
|
||||
// json_cursor.hako — JsonCursorBox (thin scan facade)
|
||||
// Responsibility: provide a minimal, escape-aware scanning facade used by JsonFragBox
|
||||
// Delegates to StringOps, StringScanBox and JsonScanBox.
|
||||
|
||||
using "lang/src/shared/common/string_ops.hako" as StringOps
|
||||
using "lang/src/shared/json/core/string_scan.hako" as StringScanBox
|
||||
using "lang/src/shared/json/core/json_scan.hako" as JsonScanBox
|
||||
|
||||
static box JsonCursorBox {
|
||||
index_of_from(hay, needle, pos) {
|
||||
return StringOps.index_of_from(hay, needle, pos)
|
||||
}
|
||||
// Alias: find_from (compat)
|
||||
find_from(hay, needle, pos) { return me.index_of_from(hay, needle, pos) }
|
||||
scan_string_end(text, quote_pos) { return StringScanBox.scan_string_end(text, quote_pos) }
|
||||
seek_array_end(text, lbracket_pos) { return JsonScanBox.seek_array_end(text, lbracket_pos) }
|
||||
seek_obj_end(text, start) { return JsonScanBox.seek_obj_end(text, start) }
|
||||
find_key_dual(text, plain, escaped, pos) { return JsonScanBox.find_key_dual(text, plain, escaped, pos) }
|
||||
// Extract a sequence of optional sign + digits starting at start_pos
|
||||
digits_from(text, start_pos) {
|
||||
if text == null { return "" }
|
||||
local i = start_pos
|
||||
local n = text.size()
|
||||
local out = ""
|
||||
if i < n {
|
||||
local ch = text.substring(i, i+1)
|
||||
if ch == "-" {
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
loop(i < n) {
|
||||
local ch = text.substring(i, i+1)
|
||||
if ch == "" { break }
|
||||
if ch >= "0" && ch <= "9" {
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
} else { break }
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
static box JsonCursorMain { main(args){ return 0 } }
|
||||
25
lang/src/shared/json/json_inst_encode_box.hako
Normal file
25
lang/src/shared/json/json_inst_encode_box.hako
Normal file
@ -0,0 +1,25 @@
|
||||
// json_inst_encode_box.hako — Encode one MIR instruction Map -> JSON text (single return)
|
||||
|
||||
static box JsonInstEncodeBox {
|
||||
_i(n) { return "" + n }
|
||||
_q(s) { return s }
|
||||
|
||||
encode(node) {
|
||||
if node == null { return "{}" }
|
||||
local op = node.get("op")
|
||||
return match op {
|
||||
"const" => "{\"op\":\"const\",\"dst\":" + me._i(node.get("dst")) + ",\"value\":{\"type\":\"i64\",\"value\":" + me._i(node.get("value").get("value")) + "}}"
|
||||
"compare" => "{\"op\":\"compare\",\"cmp\":" + me._q(node.get("cmp")) + ",\"lhs\":" + me._i(node.get("lhs")) + ",\"rhs\":" + me._i(node.get("rhs")) + ",\"dst\":" + me._i(node.get("dst")) + "}"
|
||||
"binop" => "{\"op\":\"binop\",\"op_kind\":" + me._q(node.get("op_kind")) + ",\"lhs\":" + me._i(node.get("lhs")) + ",\"rhs\":" + me._i(node.get("rhs")) + ",\"dst\":" + me._i(node.get("dst")) + "}"
|
||||
"branch" => "{\"op\":\"branch\",\"cond\":" + me._i(node.get("cond")) + ",\"then\":" + me._i(node.get("then")) + ",\"else\":" + me._i(node.get("else_id")) + "}"
|
||||
"jump" => "{\"op\":\"jump\",\"target\":" + me._i(node.get("target")) + "}"
|
||||
"ret" => "{\"op\":\"ret\",\"value\":" + me._i(node.get("value")) + "}"
|
||||
"copy" => "{\"op\":\"copy\",\"dst\":" + me._i(node.get("dst")) + ",\"src\":" + me._i(node.get("src")) + "}"
|
||||
"call" => "{\"op\":\"call\",\"name\":" + me._q(node.get("name")) + ",\"args\":" + node.get("args_text") + ",\"dst\":" + me._i(node.get("dst")) + "}"
|
||||
"boxcall" => "{\"op\":\"boxcall\",\"method\":" + me._q(node.get("method")) + ",\"recv\":" + me._i(node.get("recv")) + ",\"args\":" + node.get("args_text") + ",\"dst\":" + me._i(node.get("dst")) + "}"
|
||||
"newbox" => "{\"op\":\"newbox\",\"box_type\":" + me._q(node.get("box_type")) + ",\"args\":" + node.get("args_text") + ",\"dst\":" + me._i(node.get("dst")) + "}"
|
||||
"mir_call" => "{\"op\":\"mir_call\"}"
|
||||
_ => "{}"
|
||||
}
|
||||
}
|
||||
}
|
||||
211
lang/src/shared/json/json_utils.hako
Normal file
211
lang/src/shared/json/json_utils.hako
Normal file
@ -0,0 +1,211 @@
|
||||
// JsonUtilsBox — JSON読み取りユーティリティの共通箱
|
||||
// 責務: JSON値抽出・JSON構造パース・トップレベル配列分割
|
||||
// Extracted from JsonProgramBox (480行 → 345行, 削減 ~135行)
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
|
||||
static box JsonUtilsBox {
|
||||
// Extract JSON value by key (returns value with '@' + end position marker)
|
||||
extract_value(json, key) {
|
||||
if json == null { return null }
|
||||
local pattern = "\"" + key + "\""
|
||||
local idx = StringHelpers.index_of(json, 0, pattern)
|
||||
if idx < 0 { return null }
|
||||
idx = idx + pattern.size()
|
||||
idx = StringHelpers.skip_ws(json, idx)
|
||||
if json.substring(idx, idx + 1) != ":" { return null }
|
||||
idx = StringHelpers.skip_ws(json, idx + 1)
|
||||
local res = me.read_value(json, idx)
|
||||
local at = StringHelpers.last_index_of(res, "@")
|
||||
if at < 0 { return null }
|
||||
return res.substring(0, at)
|
||||
}
|
||||
|
||||
// Extract string value by key with default fallback
|
||||
extract_string_value(json, key, default_value) {
|
||||
local raw = me.extract_value(json, key)
|
||||
if raw == null { return default_value }
|
||||
local trimmed = StringHelpers.trim(raw)
|
||||
if trimmed.size() >= 2 && trimmed.substring(0,1) == "\"" && trimmed.substring(trimmed.size()-1, trimmed.size()) == "\"" {
|
||||
return me.unescape_string(trimmed.substring(1, trimmed.size()-1))
|
||||
}
|
||||
return default_value
|
||||
}
|
||||
|
||||
// Read JSON value (dispatch to appropriate reader)
|
||||
read_value(json, idx) {
|
||||
local n = json.size()
|
||||
if idx >= n { return "@" + StringHelpers.int_to_str(idx) }
|
||||
local ch = json.substring(idx, idx + 1)
|
||||
if ch == "\"" { return me.read_string(json, idx) }
|
||||
if ch == "{" { return me.read_object(json, idx) }
|
||||
if ch == "[" { return me.read_array(json, idx) }
|
||||
return me.read_literal(json, idx)
|
||||
}
|
||||
|
||||
// Read JSON string (escape-aware) with position marker
|
||||
read_string(json, idx) {
|
||||
local i = idx + 1
|
||||
local n = json.size()
|
||||
local done = 0
|
||||
loop(done == 0 && i < n) {
|
||||
local ch = json.substring(i, i + 1)
|
||||
if ch == "\\" {
|
||||
i = i + 2
|
||||
} else {
|
||||
if ch == "\"" { done = 1 i = i + 1 } else { i = i + 1 }
|
||||
}
|
||||
}
|
||||
return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
|
||||
}
|
||||
|
||||
// Skip JSON string (returns end position)
|
||||
skip_string(json, idx) {
|
||||
local i = idx + 1
|
||||
local n = json.size()
|
||||
local done = 0
|
||||
loop(done == 0 && i < n) {
|
||||
local ch = json.substring(i, i + 1)
|
||||
if ch == "\\" { i = i + 2 }
|
||||
else { if ch == "\"" { done = 1 i = i + 1 } else { i = i + 1 } }
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Read JSON object (bracket-aware) with position marker
|
||||
read_object(json, idx) {
|
||||
local depth = 0
|
||||
local i = idx
|
||||
local n = json.size()
|
||||
loop(i < n) {
|
||||
local ch = json.substring(i, i + 1)
|
||||
if ch == "\"" {
|
||||
i = me.skip_string(json, i)
|
||||
} else {
|
||||
if ch == "{" { depth = depth + 1 }
|
||||
else if ch == "}" {
|
||||
depth = depth - 1
|
||||
if depth == 0 { i = i + 1 break }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
|
||||
}
|
||||
|
||||
// Read JSON array (bracket-aware) with position marker
|
||||
read_array(json, idx) {
|
||||
local depth = 0
|
||||
local i = idx
|
||||
local n = json.size()
|
||||
loop(i < n) {
|
||||
local ch = json.substring(i, i + 1)
|
||||
if ch == "\"" {
|
||||
i = me.skip_string(json, i)
|
||||
} else {
|
||||
if ch == "[" { depth = depth + 1 }
|
||||
else if ch == "]" {
|
||||
depth = depth - 1
|
||||
if depth == 0 { i = i + 1 break }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
|
||||
}
|
||||
|
||||
// Read JSON literal (number/true/false/null) with position marker
|
||||
read_literal(json, idx) {
|
||||
local n = json.size()
|
||||
local i = idx
|
||||
loop(i < n) {
|
||||
local ch = json.substring(i, i + 1)
|
||||
if ch == "," || ch == "}" || ch == "]" { break }
|
||||
i = i + 1
|
||||
}
|
||||
return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
|
||||
}
|
||||
|
||||
// Split JSON array at top-level commas (depth-aware, escape-aware)
|
||||
split_top_level(array_json) {
|
||||
local out = new ArrayBox()
|
||||
local n = array_json.size()
|
||||
local i = 1
|
||||
local start = 1
|
||||
local depth = 0
|
||||
local in_string = 0
|
||||
loop(i < n - 1) {
|
||||
local ch = array_json.substring(i, i + 1)
|
||||
if in_string == 1 {
|
||||
if ch == "\\" {
|
||||
i = i + 2
|
||||
} else {
|
||||
if ch == "\"" { in_string = 0 }
|
||||
i = i + 1
|
||||
}
|
||||
} else {
|
||||
if ch == "\"" {
|
||||
in_string = 1
|
||||
i = i + 1
|
||||
} else {
|
||||
if ch == "{" || ch == "[" { depth = depth + 1 }
|
||||
else if ch == "}" || ch == "]" { depth = depth - 1 }
|
||||
else if ch == "," && depth == 0 {
|
||||
local part = array_json.substring(start, i)
|
||||
out.push(part)
|
||||
start = i + 1
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if start < n - 1 {
|
||||
out.push(array_json.substring(start, n - 1))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Unescape JSON string (convert \n, \t, \r, \\, \" to actual characters)
|
||||
unescape_string(s) {
|
||||
if s == null { return "" }
|
||||
local out = ""
|
||||
local i = 0
|
||||
local n = s.size()
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i + 1)
|
||||
if ch == "\\" && i + 1 < n {
|
||||
local nx = s.substring(i + 1, i + 2)
|
||||
if nx == "\"" {
|
||||
out = out + "\""
|
||||
i = i + 2
|
||||
} else {
|
||||
if nx == "\\" {
|
||||
out = out + "\\"
|
||||
i = i + 2
|
||||
} else {
|
||||
if nx == "n" {
|
||||
out = out + "\n"
|
||||
i = i + 2
|
||||
} else {
|
||||
if nx == "r" {
|
||||
out = out + "\r"
|
||||
i = i + 2
|
||||
} else {
|
||||
if nx == "t" {
|
||||
out = out + "\t"
|
||||
i = i + 2
|
||||
} else {
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
159
lang/src/shared/json/mir_builder2.hako
Normal file
159
lang/src/shared/json/mir_builder2.hako
Normal file
@ -0,0 +1,159 @@
|
||||
// mir_builder2.hako — Instance builder(引数渡しバグ回避のため、非staticで内部状態を保持)
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using lang.compiler.emit.common.json_emit as JsonEmitBox
|
||||
using lang.compiler.emit.common.mir_emit as MirEmitBox
|
||||
using lang.compiler.emit.common.header_emit as HeaderEmitBox
|
||||
|
||||
box MirJsonBuilder2 {
|
||||
st: MapBox
|
||||
|
||||
// ---- lifecycle ----
|
||||
setup() {
|
||||
me.st = map({
|
||||
buf: "",
|
||||
phase: 0,
|
||||
first_inst: 1,
|
||||
blocks: new ArrayBox(),
|
||||
cur_block_index: -1,
|
||||
fn_name: "",
|
||||
prefer_rebuild: 0,
|
||||
append_headers: 0,
|
||||
append_insts: 0
|
||||
})
|
||||
}
|
||||
|
||||
// ---- internal helpers ----
|
||||
_get_buf() { return me.st.get("buf") }
|
||||
_set_buf(s) { me.st.set("buf", s) }
|
||||
_append(s) { local b = me._get_buf() me._set_buf(b + s) }
|
||||
_append_header(s) {
|
||||
local on = me.st.get("append_headers")
|
||||
if on != null && on == 1 { me._append(s) }
|
||||
}
|
||||
_append_inst_json(node) {
|
||||
// Append per-instruction JSON only when explicitly enabled (dev aid)
|
||||
local on = me.st.get("append_insts")
|
||||
if on != null && on == 1 { me._append(JsonEmitBox.to_json(node)) }
|
||||
}
|
||||
_int_to_str(n) { return StringHelpers.int_to_str(n) }
|
||||
_cur_insts() {
|
||||
local blks = me.st.get("blocks")
|
||||
if blks == null || blks.size == null { return null }
|
||||
local n = blks.size()
|
||||
if n <= 0 { return null }
|
||||
local idx = me.st.get("cur_block_index")
|
||||
if idx == null || idx < 0 || idx >= n { idx = n - 1 }
|
||||
local blk = blks.get(idx)
|
||||
return blk.get("instructions")
|
||||
}
|
||||
_comma_if_needed() {
|
||||
// Only relevant when appending instruction JSON text
|
||||
local ai = me.st.get("append_insts")
|
||||
if ai == null || ai != 1 { return }
|
||||
local first = me.st.get("first_inst")
|
||||
if first == 1 { me.st.set("first_inst", 0) return }
|
||||
me._append(",")
|
||||
}
|
||||
|
||||
// ---- building API(インスタンスメソッド)----
|
||||
start_module() { me.st.set("phase", 1) me._append_header("{\"functions\":[") }
|
||||
start_function(name) {
|
||||
me.st.set("phase", 2)
|
||||
me.st.set("fn_name", name)
|
||||
me.st.set("blocks", new ArrayBox())
|
||||
me.st.set("cur_block_index", -1)
|
||||
me._append_header("{\"name\":" + StringHelpers.json_quote(name) + ",\"params\":[],\"blocks\":[")
|
||||
}
|
||||
start_block(id) {
|
||||
me.st.set("phase", 3)
|
||||
me.st.set("first_inst", 1)
|
||||
// 配列側
|
||||
local blk = map({ id: id, instructions: new ArrayBox() })
|
||||
local blks = me.st.get("blocks")
|
||||
blks.push(blk)
|
||||
me.st.set("cur_block_index", blks.size() - 1)
|
||||
// 文字列側
|
||||
me._append_header("{\"id\":" + me._int_to_str(id) + ",\"instructions\":[")
|
||||
}
|
||||
end_block_continue() { me._append_header("]},") }
|
||||
end_all() {
|
||||
me.st.set("phase", 5)
|
||||
// Prefer route to rebuild on to_string() for safer parity
|
||||
me.st.set("prefer_rebuild", 1)
|
||||
me._append_header("]}]}]}")
|
||||
}
|
||||
|
||||
add_const(dst, val) {
|
||||
me._comma_if_needed()
|
||||
// 構造
|
||||
local node = MirEmitBox.make_const(dst, val)
|
||||
local insts = me._cur_insts()
|
||||
if insts != null && insts.push != null { insts.push(node) }
|
||||
// テキスト
|
||||
me._append_inst_json(node)
|
||||
}
|
||||
add_compare(kind, lhs, rhs, dst) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_compare(kind, lhs, rhs, dst)
|
||||
local insts = me._cur_insts()
|
||||
if insts != null && insts.push != null { insts.push(node) }
|
||||
me._append_inst_json(node)
|
||||
}
|
||||
add_copy(dst, src) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_copy(dst, src)
|
||||
local insts = me._cur_insts()
|
||||
if insts != null && insts.push != null { insts.push(node) }
|
||||
me._append_inst_json(node)
|
||||
}
|
||||
add_branch(cond, then_id, else_id) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_branch(cond, then_id, else_id)
|
||||
local insts = me._cur_insts()
|
||||
if insts != null && insts.push != null { insts.push(node) }
|
||||
me._append_inst_json(node)
|
||||
}
|
||||
add_jump(target) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_jump(target)
|
||||
local insts = me._cur_insts()
|
||||
if insts != null && insts.push != null { insts.push(node) }
|
||||
me._append_inst_json(node)
|
||||
}
|
||||
add_ret(val) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_ret(val)
|
||||
local insts = me._cur_insts()
|
||||
if insts != null && insts.push != null { insts.push(node) }
|
||||
me._append_inst_json(node)
|
||||
}
|
||||
|
||||
// ---- outputs ----
|
||||
to_string() {
|
||||
local pref = me.st.get("prefer_rebuild")
|
||||
if pref != null && pref == 1 { return me.to_string_rebuild() }
|
||||
return me._get_buf()
|
||||
}
|
||||
to_string_rebuild() {
|
||||
// 構造(blocks → instructions)を正として再構築(HeaderEmitBox 経由)
|
||||
local name = me.st.get("fn_name")
|
||||
local blks = me.st.get("blocks")
|
||||
local blocks = new ArrayBox()
|
||||
if blks != null && blks.size != null {
|
||||
local n = blks.size()
|
||||
local i = 0
|
||||
loop (i < n) {
|
||||
local blk = blks.get(i)
|
||||
local bid = blk.get("id")
|
||||
local insts = blk.get("instructions")
|
||||
blocks.push(HeaderEmitBox.make_block(bid, insts))
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
local fns = new ArrayBox()
|
||||
fns.push(HeaderEmitBox.make_function_main(blocks))
|
||||
return JsonEmitBox.to_json(HeaderEmitBox.make_module_with_functions(fns))
|
||||
}
|
||||
}
|
||||
|
||||
static box MirJsonBuilder2Stub { main(args) { return 0 } }
|
||||
439
lang/src/shared/json/mir_builder_min.hako
Normal file
439
lang/src/shared/json/mir_builder_min.hako
Normal file
@ -0,0 +1,439 @@
|
||||
// mir_builder_min.nyash — Minimal MIR(JSON v0) builder for selfhost tests
|
||||
// Scope: selfhost only (apps/selfhost/...); no core/runtime changes.
|
||||
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
|
||||
using lang.compiler.emit.common.json_emit as JsonEmitBox
|
||||
using lang.compiler.emit.common.mir_emit as MirEmitBox
|
||||
using lang.compiler.emit.common.call_emit as CallEmitBox
|
||||
using lang.compiler.emit.common.newbox_emit as NewBoxEmitBox
|
||||
using "lang/src/shared/json/json_inst_encode_box.hako" as JsonInstEncodeBox
|
||||
|
||||
box MirJsonBuilderMin {
|
||||
buf: StringBox
|
||||
phase: IntegerBox
|
||||
first_inst: IntegerBox
|
||||
blocks: ArrayBox
|
||||
cur_block_index: IntegerBox
|
||||
fn_name: StringBox
|
||||
trace: IntegerBox
|
||||
verify: IntegerBox
|
||||
|
||||
birth() {
|
||||
me.buf = ""
|
||||
me.phase = 0
|
||||
me.first_inst = 1
|
||||
me.blocks = new ArrayBox()
|
||||
me.cur_block_index = -1
|
||||
me.fn_name = ""
|
||||
me.trace = 0
|
||||
me.verify = 0
|
||||
}
|
||||
|
||||
// Internal helpers
|
||||
_get_buf() { return me.buf }
|
||||
_set_buf(s) { me.buf = s return me }
|
||||
_append(s) {
|
||||
me.buf = me.buf + s
|
||||
return me
|
||||
}
|
||||
_int_to_str(n) { return StringHelpers.int_to_str(n) }
|
||||
_to_i64(x) { return StringHelpers.to_i64(x) }
|
||||
|
||||
_quote(s) { return StringHelpers.json_quote(s) }
|
||||
|
||||
_ids_range_json(start, count) {
|
||||
local i = 0
|
||||
local out = "["
|
||||
loop (i < count) {
|
||||
if i > 0 { out = out + "," }
|
||||
out = out + me._int_to_str(start + i)
|
||||
i = i + 1
|
||||
}
|
||||
out = out + "]"
|
||||
return out
|
||||
}
|
||||
|
||||
_ids_array_from_json_text(arr_text) {
|
||||
local out = new ArrayBox()
|
||||
if arr_text == null { return out }
|
||||
local s = "" + arr_text
|
||||
local i = 0
|
||||
loop (i < s.size()) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch >= "0" && ch <= "9" {
|
||||
local j = i
|
||||
loop (j < s.size()) {
|
||||
local cj = s.substring(j, j+1)
|
||||
if !(cj >= "0" && cj <= "9") { break }
|
||||
j = j + 1
|
||||
}
|
||||
local num = me._to_i64(s.substring(i, j))
|
||||
out.push(num)
|
||||
i = j
|
||||
} else {
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
_ensure_phase(want) {
|
||||
if me.phase + 1 < want { return me }
|
||||
return me
|
||||
}
|
||||
|
||||
// Public builder steps
|
||||
start_module() {
|
||||
if me.trace == 1 { print("[DEBUG start_module] starting") }
|
||||
me.phase = 1
|
||||
return me._append("{\"functions\":[")
|
||||
}
|
||||
|
||||
start_function(name) {
|
||||
if me.trace == 1 { print("[DEBUG start_function] name=" + name) }
|
||||
me.phase = 2
|
||||
me.fn_name = name
|
||||
me.blocks = new ArrayBox()
|
||||
me.cur_block_index = -1
|
||||
if me.trace == 1 { print("[DEBUG start_function] after set, fn_name=" + me.fn_name) }
|
||||
local b = "{\"name\":" + me._quote(name) + ",\"params\":[],\"blocks\":["
|
||||
return me._append(b)
|
||||
}
|
||||
|
||||
start_block(id) {
|
||||
me.phase = 3
|
||||
me.first_inst = 1
|
||||
local blk = map({ id: id, instructions: new ArrayBox() })
|
||||
me.blocks.push(blk)
|
||||
me.cur_block_index = me.blocks.size() - 1
|
||||
local b = "{\"id\":" + me._int_to_str(id) + ",\"instructions\":["
|
||||
return me._append(b)
|
||||
}
|
||||
|
||||
end_block_continue() {
|
||||
return me._append("]},")
|
||||
}
|
||||
|
||||
_comma_if_needed() {
|
||||
if me.first_inst == 1 {
|
||||
me.first_inst = 0
|
||||
return me
|
||||
}
|
||||
return me._append(",")
|
||||
}
|
||||
|
||||
add_const(dst, val) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_const(dst, val)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_compare(kind, lhs, rhs, dst) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_compare(kind, lhs, rhs, dst)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_binop(kind, lhs, rhs, dst) {
|
||||
me._comma_if_needed()
|
||||
local node = { op:"binop", op_kind:kind, lhs:lhs, rhs:rhs, dst:dst }
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_ret(val) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_ret(val)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_call_ids(name, args_json_text, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = me._ids_array_from_json_text(args_json_text)
|
||||
local node = CallEmitBox.make_call(name, args, dst)
|
||||
node.set("args_text", args_json_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_call_range(name, start, count, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < count) {
|
||||
args.push(start + i)
|
||||
i = i + 1
|
||||
}
|
||||
local args_text = me._ids_range_json(start, count)
|
||||
local node = CallEmitBox.make_call(name, args, dst)
|
||||
node.set("args_text", args_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_boxcall_range(method, recv_id, args_start, count, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < count) {
|
||||
args.push(args_start + i)
|
||||
i = i + 1
|
||||
}
|
||||
local args_text = me._ids_range_json(args_start, count)
|
||||
local node = CallEmitBox.make_boxcall(method, recv_id, args, dst)
|
||||
node.set("args_text", args_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_newbox_range(box_type, args_start, count, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < count) {
|
||||
args.push(args_start + i)
|
||||
i = i + 1
|
||||
}
|
||||
local node = NewBoxEmitBox.with_args_array(NewBoxEmitBox.make_new(box_type, args, dst), args)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_newbox_ids(box_type, args_json_text, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = me._ids_array_from_json_text(args_json_text)
|
||||
local node = NewBoxEmitBox.make_new(box_type, args, dst)
|
||||
node = NewBoxEmitBox.with_args_text(node, args_json_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
_args_array_json_from_ids(ids_start, count) {
|
||||
return me._ids_range_json(ids_start, count)
|
||||
}
|
||||
|
||||
add_mir_call_global_ids(name, args_json_text, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = me._ids_array_from_json_text(args_json_text)
|
||||
local node = CallEmitBox.make_mir_call_global(name, args, dst)
|
||||
node.set("args_text", args_json_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_mir_call_extern_ids(name, args_json_text, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = me._ids_array_from_json_text(args_json_text)
|
||||
local node = CallEmitBox.make_mir_call_extern(name, args, dst)
|
||||
node.set("args_text", args_json_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_mir_call_method_ids(method, recv_id, args_json_text, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = me._ids_array_from_json_text(args_json_text)
|
||||
local node = CallEmitBox.make_mir_call_method(method, recv_id, args, dst)
|
||||
node.set("args_text", args_json_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_mir_call_constructor_ids(box_type, args_json_text, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = me._ids_array_from_json_text(args_json_text)
|
||||
local node = CallEmitBox.make_mir_call_constructor(box_type, args, dst)
|
||||
node.set("args_text", args_json_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_mir_call_global_range(name, start, count, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < count) {
|
||||
args.push(start + i)
|
||||
i = i + 1
|
||||
}
|
||||
local args_text = me._ids_range_json(start, count)
|
||||
local node = CallEmitBox.make_mir_call_global(name, args, dst)
|
||||
node.set("args_text", args_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_mir_call_extern_range(name, start, count, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < count) { args.push(start + i) i = i + 1 }
|
||||
local args_text = me._ids_range_json(start, count)
|
||||
local node = CallEmitBox.make_mir_call_extern(name, args, dst)
|
||||
node.set("args_text", args_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_mir_call_method_range(method, recv_id, args_start, count, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < count) {
|
||||
args.push(args_start + i)
|
||||
i = i + 1
|
||||
}
|
||||
local args_text = me._ids_range_json(args_start, count)
|
||||
local node = CallEmitBox.make_mir_call_method(method, recv_id, args, dst)
|
||||
node.set("args_text", args_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_mir_call_constructor_range(box_type, args_start, count, dst) {
|
||||
me._comma_if_needed()
|
||||
local args = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < count) {
|
||||
args.push(args_start + i)
|
||||
i = i + 1
|
||||
}
|
||||
local args_text = me._ids_range_json(args_start, count)
|
||||
local node = CallEmitBox.make_mir_call_constructor(box_type, args, dst)
|
||||
node.set("args_text", args_text)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_branch(cond, then_id, else_id) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_branch(cond, then_id, else_id)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_jump(target) {
|
||||
me._comma_if_needed()
|
||||
local node = MirEmitBox.make_jump(target)
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
add_copy(dst, src) {
|
||||
me._comma_if_needed()
|
||||
local node = { op:"copy", dst:dst, src:src }
|
||||
me._push_inst_map(node)
|
||||
return me._append(JsonEmitBox.to_json(node))
|
||||
}
|
||||
|
||||
end_all() {
|
||||
me.phase = 5
|
||||
return me._append("]}]}]}")
|
||||
}
|
||||
|
||||
to_string() { return me._get_buf() }
|
||||
|
||||
_cur_insts() {
|
||||
local blks = me.blocks
|
||||
if blks == null { return null }
|
||||
local n = 0
|
||||
n = BoxHelpers.array_len(blks)
|
||||
if n <= 0 { return null }
|
||||
local idx = me.cur_block_index
|
||||
if idx == null { idx = -1 }
|
||||
if idx < 0 || idx >= n { idx = n - 1 }
|
||||
local blk = BoxHelpers.array_get(blks, idx)
|
||||
if blk == null { return null }
|
||||
return BoxHelpers.map_get(blk, "instructions")
|
||||
}
|
||||
|
||||
verify_builder_state() { return 0 }
|
||||
|
||||
_verify_enabled() {
|
||||
return me.verify
|
||||
}
|
||||
|
||||
_trace_enabled() {
|
||||
return me.trace
|
||||
}
|
||||
|
||||
_push_inst(node) {
|
||||
local before = 0
|
||||
local insts0 = me._cur_insts()
|
||||
before = BoxHelpers.array_len(insts0)
|
||||
if insts0 == null || insts0.push == null { return }
|
||||
insts0.push(node)
|
||||
if me._verify_enabled() == 1 {
|
||||
local insts1 = me._cur_insts()
|
||||
local after = BoxHelpers.array_len(insts1)
|
||||
local op_str = BoxHelpers.map_get(node, "op")
|
||||
if after != before + 1 { print("[builder-verify] size mismatch before=" + before + " after=" + after + " op=" + op_str) }
|
||||
}
|
||||
if me._trace_enabled() == 1 {
|
||||
local op_str_trace = BoxHelpers.map_get(node, "op")
|
||||
print("[builder-trace] push op=" + op_str_trace)
|
||||
}
|
||||
}
|
||||
|
||||
_push_inst_map(node) { return me._push_inst(node) }
|
||||
|
||||
enable_trace(on) {
|
||||
if on == null { on = 1 }
|
||||
me.trace = on
|
||||
return me
|
||||
}
|
||||
|
||||
enable_verify(on) {
|
||||
if on == null { on = 1 }
|
||||
me.verify = on
|
||||
return me
|
||||
}
|
||||
|
||||
get_current_instructions() { return me._cur_insts() }
|
||||
|
||||
get_blocks_array() { return me.blocks }
|
||||
|
||||
get_function_structure() {
|
||||
local blks = me.get_blocks_array()
|
||||
return map({ name: me.fn_name, params: new ArrayBox(), blocks: blks })
|
||||
}
|
||||
|
||||
emit_to_string() { return me.to_string() }
|
||||
|
||||
_inst_json(node) { return JsonInstEncodeBox.encode(node) }
|
||||
|
||||
to_string_rebuild() {
|
||||
local name = me.fn_name
|
||||
local blks = me.get_blocks_array()
|
||||
local blks_size_str = "null"
|
||||
local blks_len = BoxHelpers.array_len(blks)
|
||||
if blks_len >= 0 { blks_size_str = me._int_to_str(blks_len) }
|
||||
print("[DEBUG rebuild] fn_name=" + name + " blks.size()=" + blks_size_str)
|
||||
local out = "{\"functions\":[{\"name\":" + me._quote(name) + ",\"params\":[],\"blocks\":["
|
||||
local n = blks_len
|
||||
print("[DEBUG rebuild] n=" + me._int_to_str(n))
|
||||
local i = 0
|
||||
loop (i < n) {
|
||||
if i > 0 { out = out + "," }
|
||||
local blk = BoxHelpers.array_get(blks, i)
|
||||
local bid = BoxHelpers.map_get(blk, "id")
|
||||
out = out + "{\"id\":" + me._int_to_str(bid) + ",\"instructions\":["
|
||||
local insts = BoxHelpers.map_get(blk, "instructions")
|
||||
local m = BoxHelpers.array_len(insts)
|
||||
local j = 0
|
||||
loop (j < m) {
|
||||
if j > 0 { out = out + "," }
|
||||
out = out + me._inst_json(BoxHelpers.array_get(insts, j))
|
||||
j = j + 1
|
||||
}
|
||||
out = out + "]}"
|
||||
i = i + 1
|
||||
}
|
||||
out = out + "]}]}"
|
||||
return out
|
||||
}
|
||||
}
|
||||
119
lang/src/shared/json/mir_v1_adapter.hako
Normal file
119
lang/src/shared/json/mir_v1_adapter.hako
Normal file
@ -0,0 +1,119 @@
|
||||
// mir_v1_adapter.nyash — Minimal JSON v1 (mir_call) to v0 adapter for selfhost
|
||||
// Scope: selfhost only. Transforms op:"mir_call" into legacy v0 ops (call/boxcall/newbox).
|
||||
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
|
||||
static box MirJsonV1Adapter {
|
||||
// Delegate to JsonCursorBox (escape-aware implementations, fixes 2 escape bugs)
|
||||
_index_of_from(hay, needle, pos) {
|
||||
return JsonCursorBox.index_of_from(hay, needle, pos)
|
||||
}
|
||||
_seek_obj_end(text, obj_start) {
|
||||
return JsonCursorBox.seek_obj_end(text, obj_start)
|
||||
}
|
||||
_seek_array_end(text, pos_after_bracket) {
|
||||
// JsonCursorBox.seek_array_end expects pos at '[', adjust since we receive pos after '['
|
||||
if text == null { return -1 }
|
||||
if pos_after_bracket <= 0 { return -1 }
|
||||
local bracket_pos = pos_after_bracket - 1
|
||||
return JsonCursorBox.seek_array_end(text, bracket_pos)
|
||||
}
|
||||
_read_digits(text, pos) {
|
||||
return JsonCursorBox.digits_from(text, pos)
|
||||
}
|
||||
_read_string(text, pos_after_quote) {
|
||||
local end = me._index_of_from(text, "\"", pos_after_quote)
|
||||
if end < 0 { return "" }
|
||||
return text.substring(pos_after_quote, end)
|
||||
}
|
||||
|
||||
// Convert one mir_call object JSON segment to v0 legacy op JSON
|
||||
_convert_mir_call(seg) {
|
||||
// dst
|
||||
local dpos = me._index_of_from(seg, "\"dst\":", 0)
|
||||
local dst = 0
|
||||
if dpos >= 0 {
|
||||
local dd = me._read_digits(seg, dpos + 6)
|
||||
if dd != "" { dst = 0 + ("" + dd).size() // placeholder to avoid lints
|
||||
dst = 0 // reassign after parse
|
||||
// parse int
|
||||
local acc = 0
|
||||
local i = 0
|
||||
loop(i < dd.size()) { acc = acc * 10 + ("0123456789".indexOf(dd.substring(i,i+1))) i = i + 1 }
|
||||
dst = acc
|
||||
}
|
||||
}
|
||||
// args array substring (reuse existing as-is)
|
||||
local ak = me._index_of_from(seg, "\"args\":[", 0)
|
||||
local args_json = "[]"
|
||||
if ak >= 0 {
|
||||
local lb = me._index_of_from(seg, "[", ak)
|
||||
if lb >= 0 {
|
||||
local rb = me._seek_array_end(seg, lb + 1)
|
||||
if rb > lb { args_json = seg.substring(lb, rb + 1) }
|
||||
}
|
||||
}
|
||||
// callee type
|
||||
local ck = me._index_of_from(seg, "\"callee\":{\"type\":\"", 0)
|
||||
if ck < 0 { return seg }
|
||||
local ct = me._read_string(seg, ck + 18)
|
||||
if ct == "Global" {
|
||||
local nk = me._index_of_from(seg, "\"name\":\"", ck)
|
||||
local name = ""
|
||||
if nk >= 0 { name = me._read_string(seg, nk + 8) }
|
||||
return "{\"op\":\"call\",\"name\":\"" + name + "\",\"args\":" + args_json + ",\"dst\":" + dst + "}"
|
||||
}
|
||||
if ct == "Method" {
|
||||
local mk = me._index_of_from(seg, "\"method\":\"", ck)
|
||||
local method = ""
|
||||
if mk >= 0 { method = me._read_string(seg, mk + 10) }
|
||||
local rk = me._index_of_from(seg, "\"receiver\":", ck)
|
||||
local recv = 0
|
||||
if rk >= 0 {
|
||||
local rd = me._read_digits(seg, rk + 11)
|
||||
if rd != "" {
|
||||
local acc2 = 0
|
||||
local i2 = 0
|
||||
loop(i2 < rd.size()) { acc2 = acc2 * 10 + ("0123456789".indexOf(rd.substring(i2,i2+1))) i2 = i2 + 1 }
|
||||
recv = acc2
|
||||
}
|
||||
}
|
||||
return "{\"op\":\"boxcall\",\"method\":\"" + method + "\",\"recv\":" + recv + ",\"args\":" + args_json + ",\"dst\":" + dst + "}"
|
||||
}
|
||||
if ct == "Constructor" {
|
||||
local tk = me._index_of_from(seg, "\"box_type\":\"", ck)
|
||||
local tname = ""
|
||||
if tk >= 0 { tname = me._read_string(seg, tk + 12) }
|
||||
return "{\"op\":\"newbox\",\"box_type\":\"" + tname + "\",\"args\":" + args_json + ",\"dst\":" + dst + "}"
|
||||
}
|
||||
// default: return original segment
|
||||
return seg
|
||||
}
|
||||
|
||||
// Public API: convert all mir_call objects within MIR(JSON v1) to v0 legacy ops
|
||||
to_v0(mjson) {
|
||||
if mjson == null { return null }
|
||||
local out = mjson
|
||||
local cur = 0
|
||||
loop(true) {
|
||||
local op = me._index_of_from(out, "\"op\":\"mir_call\"", cur)
|
||||
if op < 0 { break }
|
||||
// find object bounds
|
||||
local start = op
|
||||
loop(start >= 0) {
|
||||
if out.substring(start, start+1) == "{" { break }
|
||||
start = start - 1
|
||||
}
|
||||
if start < 0 { break }
|
||||
local end = me._seek_obj_end(out, start)
|
||||
if end <= start { break }
|
||||
local seg = out.substring(start, end)
|
||||
local rep = me._convert_mir_call(seg)
|
||||
out = out.substring(0, start) + rep + out.substring(end, out.size())
|
||||
cur = start + rep.size()
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
static box MirJsonV1AdapterStub { main(args) { return 0 } }
|
||||
26
lang/src/shared/json/mir_v1_meta_inject_box.hako
Normal file
26
lang/src/shared/json/mir_v1_meta_inject_box.hako
Normal file
@ -0,0 +1,26 @@
|
||||
// mir_v1_meta_inject_box.hako — Inject metadata.extern_c and v1 header into MIR(JSON)
|
||||
// Responsibility: Convert a minimal v0-like MIR JSON (with only {"functions":[...]})
|
||||
// into a v1 root with kind/schema_version/metadata.extern_c while preserving functions.
|
||||
|
||||
static box MirV1MetaInjectBox {
|
||||
// Injects: {"kind":"MIR","schema_version":"1.0","metadata":{"extern_c":EXTERNS}, ...}
|
||||
// - mir_json must be an object JSON starting with '{' and ending with '}'
|
||||
// - externs_json must be a JSON array text (e.g., "[]" or "[{\"func\":...,\"symbol\":...}]")
|
||||
inject_meta_externs(mir_json, externs_json) {
|
||||
if mir_json == null { return null }
|
||||
local s = "" + mir_json
|
||||
if s.size() < 2 { return s }
|
||||
if s.substring(0,1) != "{" { return s }
|
||||
local ext = externs_json
|
||||
if ext == null { ext = "[]" }
|
||||
if ext.size() == 0 { ext = "[]" }
|
||||
// Construct v1 root by inserting header + metadata at the beginning
|
||||
// { <insert here> existing_without_leading_brace
|
||||
local tail = s.substring(1, s.size())
|
||||
local head = "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"metadata\":{\"extern_c\":" + ext + "},"
|
||||
return head + tail
|
||||
}
|
||||
}
|
||||
|
||||
static box MirV1MetaInjectMain { main(args) { return 0 } }
|
||||
|
||||
74
lang/src/shared/json/utils/json_frag.hako
Normal file
74
lang/src/shared/json/utils/json_frag.hako
Normal file
@ -0,0 +1,74 @@
|
||||
// json_frag.hako — JSON v0 断片抽出ユーティリティ(Box)
|
||||
// 責務: 文字列JSONから key:int / key:str を簡便に取り出す。
|
||||
// 非責務: 実行・評価(構造検査やVM実行は他箱に委譲)。
|
||||
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
|
||||
static box JsonFragBox {
|
||||
// 基本ヘルパ
|
||||
index_of_from(hay, needle, pos) { return JsonCursorBox.index_of_from(hay, needle, pos) }
|
||||
read_digits(text, pos) { return StringHelpers.read_digits(text, pos) }
|
||||
_str_to_int(s) { return StringHelpers.to_i64(s) }
|
||||
|
||||
// key に続く数値(最初の一致)を返す。見つからなければ null。
|
||||
get_int(seg, key) {
|
||||
local pat1 = "\"" + key + "\":"
|
||||
local p = me.index_of_from(seg, pat1, 0)
|
||||
if p >= 0 {
|
||||
local v = me.read_digits(seg, p + pat1.size())
|
||||
if v != "" { return me._str_to_int(v) }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// key に続く "..." の文字列(最初の一致)を返す。見つからなければ空文字。
|
||||
get_str(seg, key) {
|
||||
local pat = "\"" + key + "\":\""
|
||||
local p = me.index_of_from(seg, pat, 0)
|
||||
if p >= 0 {
|
||||
local vstart = p + pat.size() // start of value (right after opening quote)
|
||||
local vend = JsonCursorBox.scan_string_end(seg, vstart - 1)
|
||||
if vend > vstart { return seg.substring(vstart, vend) }
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Strict variants: emit an error when the key is missing
|
||||
get_int_strict(seg, key) {
|
||||
local v = me.get_int(seg, key)
|
||||
if v == null {
|
||||
print("[ERROR] Missing key: " + key)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
get_str_strict(seg, key) {
|
||||
local v = me.get_str(seg, key)
|
||||
if v == "" {
|
||||
print("[ERROR] Missing key: " + key)
|
||||
}
|
||||
return v
|
||||
}
|
||||
// ブロック0の instructions を丸ごと返す(配列の中身のみ返す)。
|
||||
block0_segment(mjson) {
|
||||
if mjson == null { return "" }
|
||||
// Find the instructions array start reliably
|
||||
local key = "\"instructions\":["
|
||||
local pk = call("String.indexOf/2", mjson, key)
|
||||
if pk < 0 { return "" }
|
||||
// '[' position
|
||||
local arr_bracket = pk + key.size() - 1
|
||||
// Use escape-aware scanner to find matching ']'
|
||||
local endp = JsonCursorBox.seek_array_end(mjson, arr_bracket)
|
||||
if endp < 0 { return "" }
|
||||
return mjson.substring(arr_bracket + 1, endp)
|
||||
}
|
||||
|
||||
|
||||
// Alias for legacy/buggy resolvers that drop underscores in method names.
|
||||
// Keep as a thin forwarder to preserve strict naming in source while
|
||||
// unblocking runtimes that accidentally call `block0segment`.
|
||||
}
|
||||
18
lang/src/shared/json_adapter.hako
Normal file
18
lang/src/shared/json_adapter.hako
Normal file
@ -0,0 +1,18 @@
|
||||
// Adapter for JSON cursor operations (extracted)
|
||||
// Wraps MiniJsonCur and exposes a stable facade
|
||||
using "lang/src/vm/boxes/json_cur.hako" as MiniJsonCur
|
||||
|
||||
static box MiniJson {
|
||||
read_quoted_from(s, pos) {
|
||||
local cur = new MiniJsonCur()
|
||||
return cur.read_quoted_from(s, pos)
|
||||
}
|
||||
read_digits_from(s, pos) {
|
||||
local cur = new MiniJsonCur()
|
||||
return cur.read_digits_from(s, pos)
|
||||
}
|
||||
next_non_ws(s, pos) {
|
||||
local cur = new MiniJsonCur()
|
||||
return cur.next_non_ws(s, pos)
|
||||
}
|
||||
}
|
||||
21
lang/src/shared/mir/README.md
Normal file
21
lang/src/shared/mir/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# selfhost/shared/mir — JSON emit / LoopForm helpers
|
||||
|
||||
責務
|
||||
- MIR(JSON v0) の構築・出力(Gate C 互換)。
|
||||
- ループ構造(LoopForm: Header/Latch/Exit)の最小組み立て。
|
||||
|
||||
方針(型依存ロジックの統一)
|
||||
- Array の長さ: `ArrayBox.size/1` → `StringHelpers.to_i64` で整数化。
|
||||
- Map の取得: `MapBox.get/2`(`type`/`value` 等)でアクセス。
|
||||
- 整数化: `StringHelpers.to_i64` を使用。文字列ヒューリスティックは禁止。
|
||||
- JSON 文字列化: `StringHelpers.json_quote` を使用(安全なエスケープ)。
|
||||
|
||||
禁止(Fail-Fast 防止のため)
|
||||
- `.getField` 相当のフィールド直参照の混入。
|
||||
- 文字列化結果からの数値抽出(`"MapBox("` 判定など)。
|
||||
|
||||
関連
|
||||
- `json_emit_box.hako`: Gate C JSON 出力(numbers unwrapped)。
|
||||
- `mir_schema_box.hako`: MIR(JSON) 構築ヘルパー(v0 スキーマ)。
|
||||
- `loop_form_box.hako`: LoopForm 構造の最小組み立て。
|
||||
|
||||
409
lang/src/shared/mir/block_builder_box.hako
Normal file
409
lang/src/shared/mir/block_builder_box.hako
Normal file
@ -0,0 +1,409 @@
|
||||
// selfhost/shared/mir/block_builder_box.hako
|
||||
// BlockBuilderBox — small helpers to assemble P1 shapes
|
||||
|
||||
using "lang/src/shared/mir/mir_schema_box.hako" as MirSchemaBox
|
||||
using "lang/src/shared/mir/loop_form_box.hako" as LoopFormBox
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
|
||||
static box BlockBuilderBox {
|
||||
|
||||
_array_len(arr) {
|
||||
if arr == null { return 0 }
|
||||
return StringHelpers.to_i64(call("ArrayBox.size/1", arr))
|
||||
}
|
||||
|
||||
_unwrap_vid(val) {
|
||||
if val == null { return null }
|
||||
local repr = "" + val
|
||||
if repr.indexOf("MapBox(") == 0 {
|
||||
local inner = call("MapBox.get/2", val, "value")
|
||||
if inner != null { return StringHelpers.to_i64(inner) }
|
||||
}
|
||||
return StringHelpers.to_i64(val)
|
||||
}
|
||||
|
||||
// Ensure every block ends with a terminator (ret/jump/branch/throw)
|
||||
_ensure_terminators(mod_full) {
|
||||
if mod_full == null { return mod_full }
|
||||
local fns = call("MapBox.get/2", mod_full, "functions")
|
||||
if fns == null { return mod_full }
|
||||
local fn_count = me._array_len(fns)
|
||||
local fi = 0
|
||||
loop(fi < fn_count) {
|
||||
local func = call("ArrayBox.get/2", fns, fi)
|
||||
if func != null {
|
||||
local blocks = call("MapBox.get/2", func, "blocks")
|
||||
if blocks != null {
|
||||
local bn = me._array_len(blocks)
|
||||
local bi = 0
|
||||
loop(bi < bn) {
|
||||
local block = call("ArrayBox.get/2", blocks, bi)
|
||||
local insts = null
|
||||
if block != null { insts = call("MapBox.get/2", block, "instructions") }
|
||||
if insts != null {
|
||||
local n = me._array_len(insts)
|
||||
if n > 0 {
|
||||
local last = call("ArrayBox.get/2", insts, n - 1)
|
||||
local op = ""
|
||||
if last != null {
|
||||
local maybe_op = call("MapBox.get/2", last, "op")
|
||||
if maybe_op != null { op = "" + maybe_op }
|
||||
}
|
||||
if !(op == "ret" || op == "return" || op == "jump" || op == "branch" || op == "throw") {
|
||||
local idx = 0
|
||||
local max_vid = -1
|
||||
local last_dst = -1
|
||||
loop(idx < n) {
|
||||
local inst = call("ArrayBox.get/2", insts, idx)
|
||||
local dst_map = null
|
||||
if inst != null { dst_map = call("MapBox.get/2", inst, "dst") }
|
||||
local vid = me._unwrap_vid(dst_map)
|
||||
if vid != null {
|
||||
if vid > max_vid { max_vid = vid }
|
||||
last_dst = vid
|
||||
}
|
||||
idx = idx + 1
|
||||
}
|
||||
if last_dst < 0 {
|
||||
last_dst = max_vid + 1
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_const(last_dst, 0))
|
||||
}
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_ret(last_dst))
|
||||
}
|
||||
} else {
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_const(0, 0))
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_ret(0))
|
||||
}
|
||||
}
|
||||
bi = bi + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
fi = fi + 1
|
||||
}
|
||||
return mod_full
|
||||
}
|
||||
// const→ret (returns literal)
|
||||
const_ret(val) {
|
||||
local insts = new ArrayBox()
|
||||
// Public name route: MirSchemaBox.* helpers
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_const(1, val))
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_ret(1))
|
||||
local blocks = new ArrayBox()
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(0, insts))
|
||||
local module = MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
return me._ensure_terminators(module)
|
||||
}
|
||||
|
||||
_ops_str_from_insts(insts) {
|
||||
if insts == null { return "" }
|
||||
local i = 0; local out = ""
|
||||
local count = me._array_len(insts)
|
||||
loop(i < count) {
|
||||
if i > 0 { out = out + "," }
|
||||
local it = call("ArrayBox.get/2", insts, i)
|
||||
if it != null {
|
||||
local op = call("MapBox.get/2", it, "op")
|
||||
if op != null { out = out + op } else { out = out + "?" }
|
||||
} else { out = out + "?" }
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
_ops_str_from_blocks(blocks) {
|
||||
if blocks == null { return "" }
|
||||
local i = 0; local all = ""
|
||||
local count = me._array_len(blocks)
|
||||
loop(i < count) {
|
||||
if i > 0 { all = all + "|" }
|
||||
local b = call("ArrayBox.get/2", blocks, i)
|
||||
local insts = null
|
||||
if b != null { insts = call("MapBox.get/2", b, "instructions") }
|
||||
all = all + me._ops_str_from_insts(insts)
|
||||
i = i + 1
|
||||
}
|
||||
return all
|
||||
}
|
||||
|
||||
const_ret_ops(val) {
|
||||
// Op-shape helper (no MIR dependency): const → ret
|
||||
// Returns a stable summary string for lightweight tests
|
||||
return "const,ret"
|
||||
}
|
||||
|
||||
// compare→branch→jump→ret (diamond)
|
||||
compare_branch(lhs, rhs, cmp) {
|
||||
// entry
|
||||
local b0 = new ArrayBox()
|
||||
call("ArrayBox.push/2", b0, MirSchemaBox.inst_const(1, lhs))
|
||||
call("ArrayBox.push/2", b0, MirSchemaBox.inst_const(2, rhs))
|
||||
call("ArrayBox.push/2", b0, MirSchemaBox.inst_compare(cmp, 1, 2, 3))
|
||||
call("ArrayBox.push/2", b0, MirSchemaBox.inst_branch(3, 1, 2))
|
||||
// then/else
|
||||
local b1 = new ArrayBox(); call("ArrayBox.push/2", b1, MirSchemaBox.inst_const(6, 1)); call("ArrayBox.push/2", b1, MirSchemaBox.inst_jump(3))
|
||||
local b2 = new ArrayBox(); call("ArrayBox.push/2", b2, MirSchemaBox.inst_const(6, 0)); call("ArrayBox.push/2", b2, MirSchemaBox.inst_jump(3))
|
||||
// merge
|
||||
local b3 = new ArrayBox(); call("ArrayBox.push/2", b3, MirSchemaBox.inst_ret(6))
|
||||
local blocks = new ArrayBox()
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(0, b0))
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(1, b1))
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(2, b2))
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(3, b3))
|
||||
local module = MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
return me._ensure_terminators(module)
|
||||
}
|
||||
|
||||
// binop: const lhs/rhs; binop -> dst=3; ret 3
|
||||
binop(lhs, rhs, opk) {
|
||||
local b0 = new ArrayBox()
|
||||
call("ArrayBox.push/2", b0, MirSchemaBox.inst_const(1, lhs))
|
||||
call("ArrayBox.push/2", b0, MirSchemaBox.inst_const(2, rhs))
|
||||
call("ArrayBox.push/2", b0, MirSchemaBox.inst_binop(opk, 1, 2, 3))
|
||||
call("ArrayBox.push/2", b0, MirSchemaBox.inst_ret(3))
|
||||
local blocks = new ArrayBox(); call("ArrayBox.push/2", blocks, MirSchemaBox.block(0, b0))
|
||||
local module = MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
return me._ensure_terminators(module)
|
||||
}
|
||||
|
||||
binop_ops(lhs, rhs, opk) {
|
||||
// Op-shape helper (no MIR dependency): const,const → binop → ret
|
||||
// Returns a stable summary string for lightweight tests
|
||||
return "const,const,binop,ret"
|
||||
}
|
||||
|
||||
// loop_counter: ops summary only(配列/Map生成を伴わない純テスト用)
|
||||
loop_counter_ops(limit) {
|
||||
// Summary of operations by shape (preheader|header|body paths|latch|exit)
|
||||
// P1 intent: const,const,const,const,const,const,jump|compare,branch|binop,jump|phi,phi,jump|phi,ret
|
||||
return "const,const,const,const,const,const,jump|compare,branch|binop,jump|phi,phi,jump|phi,ret"
|
||||
}
|
||||
|
||||
// loop_counter: while (i < limit) { i = i + 1 } ; return i
|
||||
loop_counter(limit) {
|
||||
local b0 = new ArrayBox(); call("ArrayBox.push/2", b0, MirSchemaBox.inst_const(1, 0)); call("ArrayBox.push/2", b0, MirSchemaBox.inst_const(2, limit)); call("ArrayBox.push/2", b0, MirSchemaBox.inst_const(4, 1)); call("ArrayBox.push/2", b0, MirSchemaBox.inst_jump(1))
|
||||
local b1 = new ArrayBox(); call("ArrayBox.push/2", b1, MirSchemaBox.inst_compare("Lt", 1, 2, 3)); call("ArrayBox.push/2", b1, MirSchemaBox.inst_branch(3, 2, 3))
|
||||
local b2 = new ArrayBox(); call("ArrayBox.push/2", b2, MirSchemaBox.inst_binop("Add", 1, 4, 1)); call("ArrayBox.push/2", b2, MirSchemaBox.inst_jump(1))
|
||||
local b3 = new ArrayBox(); call("ArrayBox.push/2", b3, MirSchemaBox.inst_ret(1))
|
||||
local blocks = new ArrayBox();
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(0, b0)); call("ArrayBox.push/2", blocks, MirSchemaBox.block(1, b1)); call("ArrayBox.push/2", blocks, MirSchemaBox.block(2, b2)); call("ArrayBox.push/2", blocks, MirSchemaBox.block(3, b3))
|
||||
local module = MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
return me._ensure_terminators(module)
|
||||
}
|
||||
|
||||
// LoopForm variant (with PHI + structured exit) — used by LoopForm tests/bring-up
|
||||
loop_counter_loopform(limit, skip_value, break_value) {
|
||||
local module = LoopFormBox.loop_counter(limit, skip_value, break_value)
|
||||
return me._ensure_terminators(module)
|
||||
}
|
||||
|
||||
loop_counter_loopform_json(limit, skip_value, break_value) {
|
||||
local module = me.loop_counter_loopform(limit, skip_value, break_value)
|
||||
return JsonEmitBox.to_json(module)
|
||||
}
|
||||
|
||||
to_gate_c_json(module) {
|
||||
if module == null { return "" }
|
||||
// NOTE: Caller (e.g. extern_call_ival_ret) already calls _ensure_terminators
|
||||
// Double-call causes corruption. Skip normalization here.
|
||||
return JsonEmitBox.to_json(module)
|
||||
}
|
||||
|
||||
compare_branch_ops(lhs, rhs, cmp) {
|
||||
// Op-shape helper (no MIR dependency):
|
||||
// b0: const,const,compare,branch | b1: const,jump | b2: const,jump | b3: ret
|
||||
return "const,const,compare,branch|const,jump|const,jump|ret"
|
||||
}
|
||||
|
||||
// --- P3 helpers: minimal mir_call shapes ---
|
||||
// extern call (e.g., nyrt.ops.op_eq) with provided arg ids and return
|
||||
extern_call_ret(name, args_ids, dst) {
|
||||
local b0 = [ MirSchemaBox.inst_mir_call_extern(name, args_ids, dst), MirSchemaBox.inst_ret(dst) ]
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main([ MirSchemaBox.block(0, b0) ]))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
global_call_ret(name, args_ids, dst, name_literal) {
|
||||
local actual_name = name
|
||||
if name_literal != null {
|
||||
local repr = "" + name
|
||||
if repr.indexOf("ArrayBox(") == 0 {
|
||||
actual_name = name_literal
|
||||
}
|
||||
}
|
||||
local b0 = [ MirSchemaBox.inst_mir_call_global(actual_name, args_ids, dst), MirSchemaBox.inst_ret(dst) ]
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main([ MirSchemaBox.block(0, b0) ]))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
method_call_ret(method, recv_id, args_ids, dst, method_literal) {
|
||||
local actual_method = method
|
||||
if method_literal != null {
|
||||
local repr = "" + method
|
||||
if repr.indexOf("ArrayBox(") == 0 {
|
||||
actual_method = method_literal
|
||||
}
|
||||
}
|
||||
local b0 = [ MirSchemaBox.inst_mir_call_method(actual_method, recv_id, args_ids, dst), MirSchemaBox.inst_ret(dst) ]
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main([ MirSchemaBox.block(0, b0) ]))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
constructor_call_ret(box_type, args_ids, dst, box_type_literal) {
|
||||
local actual_box_type = box_type
|
||||
if box_type_literal != null {
|
||||
local repr = "" + box_type
|
||||
if repr.indexOf("ArrayBox(") == 0 {
|
||||
actual_box_type = box_type_literal
|
||||
}
|
||||
}
|
||||
local b0 = [ MirSchemaBox.inst_mir_call_constructor(actual_box_type, args_ids, dst), MirSchemaBox.inst_ret(dst) ]
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main([ MirSchemaBox.block(0, b0) ]))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
|
||||
// --- P4 thin adapters: immediate-value variants -------------------------
|
||||
// These helpers materialize integer constants for arguments/receiver and
|
||||
// then reuse the minimal call+ret shapes above. Output remains
|
||||
// { version, kind, functions: [...] } compatible.
|
||||
|
||||
extern_call_ival_ret(name, arg_vals, name_literal) {
|
||||
// Materialize r1..rN from arg_vals, call -> r(N+1), ret r(N+1)
|
||||
// Pattern: exact copy of const_ret structure (new ArrayBox(), direct inline)
|
||||
local insts = new ArrayBox()
|
||||
// Preserve literal name even if caller-side evaluation overwrote the first argument.
|
||||
local actual_name = name
|
||||
if name_literal != null {
|
||||
local name_repr = "" + name
|
||||
if name_repr.indexOf("ArrayBox(") == 0 {
|
||||
actual_name = name_literal
|
||||
}
|
||||
}
|
||||
local n = 0
|
||||
local i = 0
|
||||
local m = 0
|
||||
if arg_vals != null { m = me._array_len(arg_vals) }
|
||||
loop(i < m) {
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_const(i, call("ArrayBox.get/2", arg_vals, i)))
|
||||
i = i + 1
|
||||
}
|
||||
n = m
|
||||
local args_ids = new ArrayBox()
|
||||
i = 0
|
||||
loop(i < n) {
|
||||
call("ArrayBox.push/2", args_ids, i)
|
||||
i = i + 1
|
||||
}
|
||||
local dst = n
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_mir_call_extern(actual_name, args_ids, dst))
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_ret(dst))
|
||||
local blocks = new ArrayBox()
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(0, insts))
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
|
||||
global_call_ival_ret(name, arg_vals, name_literal) {
|
||||
local insts = new ArrayBox()
|
||||
local actual_name = name
|
||||
if name_literal != null {
|
||||
local repr = "" + name
|
||||
if repr.indexOf("ArrayBox(") == 0 {
|
||||
actual_name = name_literal
|
||||
}
|
||||
}
|
||||
local m = 0
|
||||
if arg_vals != null { m = me._array_len(arg_vals) }
|
||||
local i = 0
|
||||
loop(i < m) {
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_const(i, call("ArrayBox.get/2", arg_vals, i)))
|
||||
i = i + 1
|
||||
}
|
||||
local args_ids = new ArrayBox()
|
||||
i = 0
|
||||
loop(i < m) {
|
||||
call("ArrayBox.push/2", args_ids, i)
|
||||
i = i + 1
|
||||
}
|
||||
local dst = m
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_mir_call_global(actual_name, args_ids, dst))
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_ret(dst))
|
||||
local blocks = new ArrayBox()
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(0, insts))
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
|
||||
method_call_ival_ret(method, recv_val, arg_vals, method_literal) {
|
||||
local insts = new ArrayBox()
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_const(0, recv_val))
|
||||
local m = 0
|
||||
if arg_vals != null { m = me._array_len(arg_vals) }
|
||||
local i = 0
|
||||
loop(i < m) {
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_const(1 + i, call("ArrayBox.get/2", arg_vals, i)))
|
||||
i = i + 1
|
||||
}
|
||||
local args_ids = new ArrayBox()
|
||||
i = 0
|
||||
loop(i < m) {
|
||||
call("ArrayBox.push/2", args_ids, 1 + i)
|
||||
i = i + 1
|
||||
}
|
||||
local dst = 1 + m
|
||||
local actual_method = method
|
||||
if method_literal != null {
|
||||
local repr = "" + method
|
||||
if repr.indexOf("ArrayBox(") == 0 {
|
||||
actual_method = method_literal
|
||||
}
|
||||
}
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_mir_call_method(actual_method, 0, args_ids, dst))
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_ret(dst))
|
||||
local blocks = new ArrayBox()
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(0, insts))
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
|
||||
constructor_call_ival_ret(box_type, arg_vals, box_type_literal) {
|
||||
local insts = new ArrayBox()
|
||||
local m = 0
|
||||
if arg_vals != null { m = me._array_len(arg_vals) }
|
||||
local i = 0
|
||||
loop(i < m) {
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_const(i, call("ArrayBox.get/2", arg_vals, i)))
|
||||
i = i + 1
|
||||
}
|
||||
local args_ids = new ArrayBox()
|
||||
i = 0
|
||||
loop(i < m) {
|
||||
call("ArrayBox.push/2", args_ids, i)
|
||||
i = i + 1
|
||||
}
|
||||
local dst = m
|
||||
local actual_box_type = box_type
|
||||
if box_type_literal != null {
|
||||
local repr = "" + box_type
|
||||
if repr.indexOf("ArrayBox(") == 0 {
|
||||
actual_box_type = box_type_literal
|
||||
}
|
||||
}
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_mir_call_constructor(actual_box_type, args_ids, dst))
|
||||
call("ArrayBox.push/2", insts, MirSchemaBox.inst_ret(dst))
|
||||
local blocks = new ArrayBox()
|
||||
call("ArrayBox.push/2", blocks, MirSchemaBox.block(0, insts))
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
|
||||
|
||||
// ctor(ArrayBox) → method(size) → ret
|
||||
ctor_then_size_ret() {
|
||||
local b0 = [
|
||||
MirSchemaBox.inst_mir_call_constructor("ArrayBox", new ArrayBox(), 1),
|
||||
MirSchemaBox.inst_mir_call_method("size", 1, new ArrayBox(), 2),
|
||||
MirSchemaBox.inst_ret(2)
|
||||
]
|
||||
local _m = MirSchemaBox.module(MirSchemaBox.fn_main([ MirSchemaBox.block(0, b0) ]))
|
||||
return me._ensure_terminators(_m)
|
||||
}
|
||||
|
||||
}
|
||||
290
lang/src/shared/mir/json_emit_box.hako
Normal file
290
lang/src/shared/mir/json_emit_box.hako
Normal file
@ -0,0 +1,290 @@
|
||||
// selfhost/shared/mir/json_emit_box.hako
|
||||
// JsonEmitBox — Gate C JSON emitter (schema_version 1.0, numbers unwrapped)
|
||||
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
|
||||
|
||||
static box JsonEmitBox {
|
||||
_expect_map(val, context) {
|
||||
if BoxHelpers.is_map(val) == 1 { return val }
|
||||
print("[JsonEmitBox] dev assert failed: expected MapBox for " + context)
|
||||
call("MapBox.get/2", val, "__json_emit_expect_map")
|
||||
return val
|
||||
}
|
||||
_expect_array(val, context) {
|
||||
if BoxHelpers.is_array(val) == 1 { return val }
|
||||
print("[JsonEmitBox] dev assert failed: expected ArrayBox for " + context)
|
||||
call("ArrayBox.get/2", val, 0)
|
||||
return val
|
||||
}
|
||||
_expect_i64(val, context) {
|
||||
if val == null {
|
||||
print("[JsonEmitBox] dev assert failed: expected i64 (non-null) for " + context)
|
||||
call("MapBox.get/2", val, "__json_emit_expect_i64_null")
|
||||
return 0
|
||||
}
|
||||
local repr = "" + val
|
||||
if repr.indexOf("MapBox(") == 0 {
|
||||
local inner = call("MapBox.get/2", val, "value")
|
||||
if inner != null { return inner }
|
||||
print("[JsonEmitBox] dev assert failed: missing value in " + context)
|
||||
call("MapBox.get/2", val, "__json_emit_expect_i64_value")
|
||||
return 0
|
||||
}
|
||||
# Assume numeric immediate; avoid module function coercion for safety
|
||||
return val
|
||||
}
|
||||
|
||||
// ---- helpers ------------------------------------------------------------
|
||||
|
||||
_int_str(val) {
|
||||
return StringHelpers.int_to_str(me._expect_i64(val, "integer field"))
|
||||
}
|
||||
|
||||
_emit_vid_or_null(val) {
|
||||
if val == null { return "null" }
|
||||
return me._int_str(val)
|
||||
}
|
||||
|
||||
_emit_vid_array(arr) {
|
||||
if arr == null { return "[]" }
|
||||
local n = BoxHelpers.array_len(arr)
|
||||
return "[" + me._emit_vid_array_rec(arr, 0, n) + "]"
|
||||
}
|
||||
_emit_vid_array_rec(arr, idx, len) {
|
||||
if idx >= len { return "" }
|
||||
local head = me._emit_vid_or_null(call("ArrayBox.get/2", arr, idx))
|
||||
local tail = me._emit_vid_array_rec(arr, idx + 1, len)
|
||||
if tail == "" { return head }
|
||||
return head + "," + tail
|
||||
}
|
||||
|
||||
_emit_effects(effects) {
|
||||
if effects == null { return "[]" }
|
||||
local n = BoxHelpers.array_len(effects)
|
||||
return "[" + me._emit_effects_rec(effects, 0, n) + "]"
|
||||
}
|
||||
_emit_effects_rec(effects, idx, len) {
|
||||
if idx >= len { return "" }
|
||||
local head = me._quote(call("ArrayBox.get/2", effects, idx))
|
||||
local tail = me._emit_effects_rec(effects, idx + 1, len)
|
||||
if tail == "" { return head }
|
||||
return head + "," + tail
|
||||
}
|
||||
|
||||
_emit_callee(callee) {
|
||||
if callee == null { return "{\"type\":\"Extern\",\"name\":\"\"}" }
|
||||
callee = me._expect_map(callee, "mir_call.callee")
|
||||
local ty_box = call("MapBox.get/2", callee, "type")
|
||||
local ty = ""
|
||||
if ty_box != null { ty = "" + ty_box }
|
||||
if ty == "Extern" {
|
||||
local name = call("MapBox.get/2", callee, "name")
|
||||
if name == null { name = "" }
|
||||
return "{\"type\":\"Extern\",\"name\":" + me._quote(name) + "}"
|
||||
}
|
||||
if ty == "ModuleFunction" {
|
||||
local name = call("MapBox.get/2", callee, "name")
|
||||
if name == null { name = "" }
|
||||
return "{\"type\":\"ModuleFunction\",\"name\":" + me._quote(name) + "}"
|
||||
}
|
||||
if ty == "Method" {
|
||||
local box_name = call("MapBox.get/2", callee, "box_name")
|
||||
if box_name == null { box_name = "" }
|
||||
local method = call("MapBox.get/2", callee, "method")
|
||||
if method == null { method = "" }
|
||||
local receiver = call("MapBox.get/2", callee, "receiver")
|
||||
return "{\"type\":\"Method\",\"box_name\":" + me._quote(box_name) +
|
||||
",\"method\":" + me._quote(method) + ",\"receiver\":" + me._emit_vid_or_null(receiver) + "}"
|
||||
}
|
||||
if ty == "Constructor" {
|
||||
local box_type = call("MapBox.get/2", callee, "box_type")
|
||||
if box_type == null { box_type = "" }
|
||||
return "{\"type\":\"Constructor\",\"box_type\":" + me._quote(box_type) + "}"
|
||||
}
|
||||
return "{\"type\":" + me._quote(ty) + "}"
|
||||
}
|
||||
|
||||
_emit_box_value(val) {
|
||||
if val == null { return "{\"type\":\"i64\",\"value\":0}" }
|
||||
local ty = "i64"
|
||||
local inner = val
|
||||
local repr = "" + val
|
||||
if repr.indexOf("MapBox(") == 0 {
|
||||
local ty_box = call("MapBox.get/2", val, "type")
|
||||
if ty_box != null { ty = "" + ty_box }
|
||||
local inner_box = call("MapBox.get/2", val, "value")
|
||||
if inner_box != null { inner = inner_box }
|
||||
}
|
||||
return "{\"type\":" + me._quote(ty) + ",\"value\":" + me._int_str(inner) + "}"
|
||||
}
|
||||
|
||||
_quote(s) {
|
||||
if s == null { return "\"\"" }
|
||||
return StringHelpers.json_quote("" + s)
|
||||
}
|
||||
|
||||
_emit_phi_values(values) {
|
||||
if values == null { return "[]" }
|
||||
local n = BoxHelpers.array_len(values)
|
||||
return "[" + me._emit_phi_rec(values, 0, n) + "]"
|
||||
}
|
||||
_emit_phi_rec(values, idx, len) {
|
||||
if idx >= len { return "" }
|
||||
local item = call("ArrayBox.get/2", values, idx)
|
||||
local block_id = null
|
||||
local value_id = null
|
||||
if item != null {
|
||||
block_id = call("MapBox.get/2", item, "block")
|
||||
value_id = call("MapBox.get/2", item, "value")
|
||||
}
|
||||
local head = "{\"block\":" + me._int_str(block_id) + ",\"value\":" + me._int_str(value_id) + "}"
|
||||
local tail = me._emit_phi_rec(values, idx + 1, len)
|
||||
if tail == "" { return head }
|
||||
return head + "," + tail
|
||||
}
|
||||
|
||||
_emit_inst(inst) {
|
||||
if inst == null { return "{}" }
|
||||
inst = me._expect_map(inst, "instruction")
|
||||
local op = call("MapBox.get/2", inst, "op")
|
||||
if op == null { op = "" }
|
||||
if op == "const" {
|
||||
return "{\"op\":\"const\",\"dst\":" + me._int_str(call("MapBox.get/2", inst, "dst")) +
|
||||
",\"value\":" + me._emit_box_value(call("MapBox.get/2", inst, "value")) + "}"
|
||||
}
|
||||
if op == "ret" || op == "return" {
|
||||
return "{\"op\":\"ret\",\"value\":" + me._int_str(call("MapBox.get/2", inst, "value")) + "}"
|
||||
}
|
||||
if op == "binop" {
|
||||
local kind = call("MapBox.get/2", inst, "op_kind")
|
||||
if kind == null { kind = "" }
|
||||
return "{\"op\":\"binop\",\"op_kind\":" + me._quote(kind) + ",\"lhs\":" +
|
||||
me._int_str(call("MapBox.get/2", inst, "lhs")) + ",\"rhs\":" + me._int_str(call("MapBox.get/2", inst, "rhs")) +
|
||||
",\"dst\":" + me._int_str(call("MapBox.get/2", inst, "dst")) + "}"
|
||||
}
|
||||
if op == "compare" {
|
||||
local cmp = call("MapBox.get/2", inst, "cmp")
|
||||
if cmp == null { cmp = "" }
|
||||
return "{\"op\":\"compare\",\"cmp\":" + me._quote(cmp) + ",\"lhs\":" +
|
||||
me._int_str(call("MapBox.get/2", inst, "lhs")) + ",\"rhs\":" + me._int_str(call("MapBox.get/2", inst, "rhs")) +
|
||||
",\"dst\":" + me._int_str(call("MapBox.get/2", inst, "dst")) + "}"
|
||||
}
|
||||
if op == "branch" {
|
||||
return "{\"op\":\"branch\",\"cond\":" + me._int_str(call("MapBox.get/2", inst, "cond")) +
|
||||
",\"then\":" + me._int_str(call("MapBox.get/2", inst, "then")) +
|
||||
",\"else\":" + me._int_str(call("MapBox.get/2", inst, "else")) + "}"
|
||||
}
|
||||
if op == "jump" {
|
||||
return "{\"op\":\"jump\",\"target\":" + me._int_str(call("MapBox.get/2", inst, "target")) + "}"
|
||||
}
|
||||
if op == "phi" {
|
||||
return "{\"op\":\"phi\",\"dst\":" + me._int_str(call("MapBox.get/2", inst, "dst")) +
|
||||
",\"values\":" + me._emit_phi_values(call("MapBox.get/2", inst, "values")) + "}"
|
||||
}
|
||||
if op == "mir_call" {
|
||||
// SSOT: payload("mir_call") のみを受理。トップレベルの互換フィールドは読まない。
|
||||
local payload = call("MapBox.get/2", inst, "mir_call")
|
||||
if payload == null { return "{\"op\":\"mir_call\"}" }
|
||||
payload = me._expect_map(payload, "mir_call payload")
|
||||
local dst_json = me._emit_vid_or_null(call("MapBox.get/2", inst, "dst"))
|
||||
local callee_json = me._emit_callee(call("MapBox.get/2", payload, "callee"))
|
||||
local args_box = call("MapBox.get/2", payload, "args")
|
||||
me._expect_array(args_box, "mir_call.args")
|
||||
local effects_box = call("MapBox.get/2", payload, "effects")
|
||||
me._expect_array(effects_box, "mir_call.effects")
|
||||
local args_json = me._emit_vid_array(args_box)
|
||||
local effects_json = me._emit_effects(effects_box)
|
||||
return "{\"op\":\"mir_call\",\"dst\":" + dst_json +
|
||||
",\"mir_call\":{\"callee\":" + callee_json +
|
||||
",\"args\":" + args_json +
|
||||
",\"effects\":" + effects_json + "}}"
|
||||
}
|
||||
// Fallback: emit op only (unexpected instruction kinds are outside P1 scope)
|
||||
return "{\"op\":" + me._quote(op) + "}"
|
||||
}
|
||||
|
||||
_emit_block(block) {
|
||||
if block == null { return "{\"id\":0,\"instructions\":[]}" }
|
||||
local insts = call("MapBox.get/2", block, "instructions")
|
||||
if insts == null {
|
||||
return "{\"id\":" + me._int_str(call("MapBox.get/2", block, "id")) + ",\"instructions\":[]}"
|
||||
}
|
||||
local n = BoxHelpers.array_len(insts)
|
||||
local body = me._emit_block_rec(insts, 0, n)
|
||||
return "{\"id\":" + me._int_str(call("MapBox.get/2", block, "id")) + ",\"instructions\":[" + body + "]}"
|
||||
}
|
||||
_emit_block_rec(insts, idx, len) {
|
||||
if idx >= len { return "" }
|
||||
local head = me._emit_inst(call("ArrayBox.get/2", insts, idx))
|
||||
local tail = me._emit_block_rec(insts, idx + 1, len)
|
||||
if tail == "" { return head }
|
||||
return head + "," + tail
|
||||
}
|
||||
|
||||
_emit_function(func) {
|
||||
if func == null {
|
||||
return "{\"name\":\"main\",\"blocks\":[]}"
|
||||
}
|
||||
local name = call("MapBox.get/2", func, "name")
|
||||
if name == null { name = "main" }
|
||||
// Optional fields: params (array) / flags (map)
|
||||
local params = call("MapBox.get/2", func, "params")
|
||||
local flags = call("MapBox.get/2", func, "flags")
|
||||
local blocks = call("MapBox.get/2", func, "blocks")
|
||||
if blocks == null {
|
||||
if params != null || flags != null {
|
||||
local head = "{\"name\":" + me._quote(name)
|
||||
if params != null { head = head + ",\"params\":" + JSON.stringify(params) }
|
||||
if flags != null { head = head + ",\"flags\":" + JSON.stringify(flags) }
|
||||
return head + ",\"blocks\":[]}"
|
||||
}
|
||||
return "{\"name\":" + me._quote(name) + ",\"blocks\":[]}"
|
||||
}
|
||||
local n = BoxHelpers.array_len(blocks)
|
||||
local body = me._emit_function_rec(blocks, 0, n)
|
||||
local head2 = "{\"name\":" + me._quote(name)
|
||||
if params != null { head2 = head2 + ",\"params\":" + JSON.stringify(params) }
|
||||
if flags != null { head2 = head2 + ",\"flags\":" + JSON.stringify(flags) }
|
||||
return head2 + ",\"blocks\":[" + body + "]}"
|
||||
}
|
||||
_emit_function_rec(blocks, idx, len) {
|
||||
if idx >= len { return "" }
|
||||
local head = me._emit_block(call("ArrayBox.get/2", blocks, idx))
|
||||
local tail = me._emit_function_rec(blocks, idx + 1, len)
|
||||
if tail == "" { return head }
|
||||
return head + "," + tail
|
||||
}
|
||||
|
||||
to_json(module) {
|
||||
if module == null { return "" }
|
||||
// Prefer single-function fallbackを強化: has() 未実装環境でも repr チェックで検出
|
||||
local f0 = call("MapBox.get/2", module, "functions_0")
|
||||
if f0 != null {
|
||||
local repr = "" + f0
|
||||
if repr.indexOf("MapBox(") == 0 || repr.indexOf("HostHandleBox(") == 0 {
|
||||
local one = me._emit_function(f0)
|
||||
return "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[" + one + "]}"
|
||||
}
|
||||
}
|
||||
// Legacy path: try functions array if available
|
||||
local funcs = call("MapBox.get/2", module, "functions")
|
||||
if funcs == null { return "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[]}" }
|
||||
local n = BoxHelpers.array_len(funcs)
|
||||
local body = me._emit_module_rec(funcs, 0, n)
|
||||
return "{\"kind\":\"MIR\",\"schema_version\":\"1.0\",\"functions\":[" + body + "]}"
|
||||
}
|
||||
_emit_module_rec(funcs, idx, len) {
|
||||
// Defensive fallback: some environments report size=0 for host-managed arrays.
|
||||
// If len==0 at entry, try to probe index 0 once to recover a single element.
|
||||
if idx == 0 && len == 0 {
|
||||
local first = call("ArrayBox.get/2", funcs, 0)
|
||||
if first != null { len = 1 }
|
||||
}
|
||||
if idx >= len { return "" }
|
||||
local head = me._emit_function(call("ArrayBox.get/2", funcs, idx))
|
||||
local tail = me._emit_module_rec(funcs, idx + 1, len)
|
||||
if tail == "" { return head }
|
||||
return head + "," + tail
|
||||
}
|
||||
}
|
||||
104
lang/src/shared/mir/loop_form_box.hako
Normal file
104
lang/src/shared/mir/loop_form_box.hako
Normal file
@ -0,0 +1,104 @@
|
||||
// selfhost/shared/mir/loop_form_box.hako
|
||||
// LoopFormBox — minimal loop structure builder (P2: continue/break snapshots + Exit PHI)
|
||||
|
||||
using "lang/src/shared/mir/mir_schema_box.hako" as MirSchemaBox
|
||||
|
||||
static box LoopFormBox {
|
||||
|
||||
// while (i < limit) {
|
||||
// if (i == break_value) break;
|
||||
// if (i == skip_value) continue;
|
||||
// sum = sum + i;
|
||||
// i = i + 1;
|
||||
// }
|
||||
// Builds LoopForm structure with Header/Latch PHI + Exit PHI (continue/break aware)
|
||||
loop_counter(limit, skip_value, break_value) {
|
||||
if skip_value == null { skip_value = 2 }
|
||||
if break_value == null { break_value = limit }
|
||||
|
||||
// Preheader (block 0): initialise loop-carried values and constants, then jump header
|
||||
local pre = new ArrayBox()
|
||||
pre.push(MirSchemaBox.inst_const(1, 0)) // r1 = 0 (initial i)
|
||||
pre.push(MirSchemaBox.inst_const(2, limit)) // r2 = limit
|
||||
pre.push(MirSchemaBox.inst_const(3, 1)) // r3 = step
|
||||
pre.push(MirSchemaBox.inst_const(4, 0)) // r4 = sum
|
||||
pre.push(MirSchemaBox.inst_const(5, skip_value)) // r5 = continue trigger
|
||||
pre.push(MirSchemaBox.inst_const(6, break_value))// r6 = break trigger
|
||||
pre.push(MirSchemaBox.inst_jump(1)) // goto header
|
||||
|
||||
// Header (block 1): PHI(i), PHI(sum), compare, branch
|
||||
local header = new ArrayBox()
|
||||
local phi_i_inputs = new ArrayBox()
|
||||
phi_i_inputs.push(MirSchemaBox.phi_incoming(0, 1)) // from preheader
|
||||
phi_i_inputs.push(MirSchemaBox.phi_incoming(7, 18)) // from latch
|
||||
header.push(MirSchemaBox.inst_phi(10, phi_i_inputs)) // r10 = current i
|
||||
|
||||
local phi_sum_inputs = new ArrayBox()
|
||||
phi_sum_inputs.push(MirSchemaBox.phi_incoming(0, 4)) // from preheader
|
||||
phi_sum_inputs.push(MirSchemaBox.phi_incoming(7, 19)) // from latch
|
||||
header.push(MirSchemaBox.inst_phi(11, phi_sum_inputs))// r11 = current sum
|
||||
|
||||
header.push(MirSchemaBox.inst_compare("Lt", 10, 2, 12)) // r12 = (i < limit)
|
||||
header.push(MirSchemaBox.inst_branch(12, 2, 8)) // body or exit
|
||||
|
||||
// Body entry (block 2): if (i == break) -> break else check continue
|
||||
local body_check_break = new ArrayBox()
|
||||
body_check_break.push(MirSchemaBox.inst_compare("Eq", 10, 6, 13)) // r13 = (i == break)
|
||||
body_check_break.push(MirSchemaBox.inst_branch(13, 3, 4))
|
||||
|
||||
// Break path (block 3): jump directly to exit; exit PHI snapshots current sum
|
||||
local break_block = new ArrayBox()
|
||||
break_block.push(MirSchemaBox.inst_jump(8))
|
||||
|
||||
// Continue check (block 4): if (i == skip) -> continue else body work
|
||||
local continue_check = new ArrayBox()
|
||||
continue_check.push(MirSchemaBox.inst_compare("Eq", 10, 5, 14)) // r14 = (i == skip)
|
||||
continue_check.push(MirSchemaBox.inst_branch(14, 5, 6))
|
||||
|
||||
// Continue path (block 5): snapshot sum, increment i, jump latch
|
||||
local continue_block = new ArrayBox()
|
||||
continue_block.push(MirSchemaBox.inst_binop("Add", 10, 3, 15)) // r15 = i + step
|
||||
continue_block.push(MirSchemaBox.inst_jump(7))
|
||||
|
||||
// Body work (block 6): sum += i; i += step; jump latch
|
||||
local body_block = new ArrayBox()
|
||||
body_block.push(MirSchemaBox.inst_binop("Add", 11, 10, 16)) // r16 = sum + i
|
||||
body_block.push(MirSchemaBox.inst_binop("Add", 10, 3, 17)) // r17 = i + step
|
||||
body_block.push(MirSchemaBox.inst_jump(7))
|
||||
|
||||
// Latch (block 7): PHI merge for continue/normal, then jump header
|
||||
local latch_block = new ArrayBox()
|
||||
local latch_phi_i = new ArrayBox()
|
||||
latch_phi_i.push(MirSchemaBox.phi_incoming(5, 15)) // continue path
|
||||
latch_phi_i.push(MirSchemaBox.phi_incoming(6, 17)) // body path
|
||||
latch_block.push(MirSchemaBox.inst_phi(18, latch_phi_i)) // feeds header.i
|
||||
|
||||
local latch_phi_sum = new ArrayBox()
|
||||
latch_phi_sum.push(MirSchemaBox.phi_incoming(5, 11)) // continue keeps sum
|
||||
latch_phi_sum.push(MirSchemaBox.phi_incoming(6, 16)) // body updated sum
|
||||
latch_block.push(MirSchemaBox.inst_phi(19, latch_phi_sum)) // feeds header.sum
|
||||
latch_block.push(MirSchemaBox.inst_jump(1))
|
||||
|
||||
// Exit (block 8): Merge sums from header fallthrough and break edge, then return
|
||||
local exit_vals = new ArrayBox()
|
||||
exit_vals.push(MirSchemaBox.phi_incoming(1, 11)) // normal completion
|
||||
exit_vals.push(MirSchemaBox.phi_incoming(3, 11)) // break path
|
||||
local exit_block = new ArrayBox()
|
||||
exit_block.push(MirSchemaBox.inst_phi(20, exit_vals))
|
||||
exit_block.push(MirSchemaBox.inst_ret(20))
|
||||
|
||||
// Assemble blocks
|
||||
local blocks = new ArrayBox()
|
||||
blocks.push(MirSchemaBox.block(0, pre))
|
||||
blocks.push(MirSchemaBox.block(1, header))
|
||||
blocks.push(MirSchemaBox.block(2, body_check_break))
|
||||
blocks.push(MirSchemaBox.block(3, break_block))
|
||||
blocks.push(MirSchemaBox.block(4, continue_check))
|
||||
blocks.push(MirSchemaBox.block(5, continue_block))
|
||||
blocks.push(MirSchemaBox.block(6, body_block))
|
||||
blocks.push(MirSchemaBox.block(7, latch_block))
|
||||
blocks.push(MirSchemaBox.block(8, exit_block))
|
||||
|
||||
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
||||
}
|
||||
}
|
||||
216
lang/src/shared/mir/mir_io_box.hako
Normal file
216
lang/src/shared/mir/mir_io_box.hako
Normal file
@ -0,0 +1,216 @@
|
||||
// mir_io_box.hako — MirIoBox (Phase A, Hako-side implementation)
|
||||
// Responsibility: provide minimal read/validate/cursor API using existing locator boxes.
|
||||
// Provider selection:
|
||||
// - Default: scan (string scanning, no plugin required)
|
||||
// - Optional: yyjson (JsonDoc/JsonNode via HostBridge extern) when JSON plugin is available
|
||||
// Fail‑Fast:
|
||||
// - validate() checks kind/schema/functions
|
||||
// - validate_function(): terminator required + jump/branch target existence (scan path ensured; provider path WIP)
|
||||
|
||||
using "lang/src/vm/boxes/result_box.hako" as Result
|
||||
using "lang/src/vm/boxes/result_helpers.hako" as ResultHelpers
|
||||
using "lang/src/shared/json/json_cursor.hako" as JsonCursorBox
|
||||
using "lang/src/shared/json/json_canonical_box.hako" as JsonCanonicalBox
|
||||
using "lang/src/vm/hakorune-vm/function_locator.hako" as FunctionLocatorBox
|
||||
using "lang/src/vm/hakorune-vm/blocks_locator.hako" as BlocksLocatorBox
|
||||
using "lang/src/vm/hakorune-vm/instrs_locator.hako" as InstrsLocatorBox
|
||||
using "lang/src/vm/hakorune-vm/backward_object_scanner.hako" as BackwardObjectScannerBox
|
||||
using "lang/src/vm/hakorune-vm/block_iterator.hako" as BlockIteratorBox
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
using "lang/src/shared/common/box_helpers.hako" as BoxHelpers
|
||||
|
||||
static box MirIoBox {
|
||||
// Internal: provider gate (yyjson)
|
||||
_provider_gate_on() {
|
||||
// Accept HAKO_JSON_PROVIDER or NYASH_JSON_PROVIDER (alias). Value: 'yyjson'
|
||||
// Use extern env.local.get to avoid direct field access to `env`.
|
||||
local v = call("env.local.get/1", "HAKO_JSON_PROVIDER")
|
||||
if v == null || v == "" { v = call("env.local.get/1", "NYASH_JSON_PROVIDER") }
|
||||
if v == null || v == "" { return 0 }
|
||||
if v == "yyjson" || v == "YYJSON" { return 1 }
|
||||
return 0
|
||||
}
|
||||
// Provider policy
|
||||
// - Default path uses string scanning (no plugin dependency)
|
||||
// - When JSON provider (yyjson) is available via HostBridge, provider path becomes eligible
|
||||
// - This box must remain Fail-Fast: invalid kind/schema/functions should be rejected deterministically
|
||||
// - Provider enablement is transparent; do not add silent fallbacks
|
||||
// Return canonicalized full MIR JSON (keys sorted, arrays preserve order)
|
||||
normalize(json) {
|
||||
return JsonCanonicalBox.canonicalize(json)
|
||||
}
|
||||
// Provider selection: prefer JSON provider when available; otherwise use scan locators
|
||||
// 判定は _json_root() の成否で行う(ENV不要)。
|
||||
|
||||
|
||||
|
||||
// Helper: parse JSON and return root JsonNodeBox (provider=yyjson)
|
||||
_json_root(json) {
|
||||
// Minimal provider (default OFF). When ON, attempt JsonDocBox.parse+root via HostBridge.
|
||||
if me._provider_gate_on() == 0 { return null }
|
||||
if json == null { return Result.Err("json provider: input is null") }
|
||||
// Construct JsonDocBox without ArrayBox dependency
|
||||
local doc = hostbridge.box_new0("JsonDocBox")
|
||||
if doc == null { return Result.Err("json provider: box_new failed") }
|
||||
// parse(json) — 1-arg convenience wrapper
|
||||
hostbridge.box_call1(doc, "parse", json)
|
||||
// check error()
|
||||
local err = hostbridge.box_call0(doc, "error")
|
||||
if err != null && err != "" { return Result.Err("json provider: parse failed: " + err) }
|
||||
// root() — existence確認のみ(成功時は Ok(0) を返す)
|
||||
local node = hostbridge.box_call0(doc, "root")
|
||||
if node == null { return Result.Err("json provider: root failed") }
|
||||
return Result.Ok(0)
|
||||
}
|
||||
validate(json) {
|
||||
if json == null { return Result.Err("null json") }
|
||||
// Optional: canonicalize full MIR JSON at ingress (identity/no-op for now)
|
||||
json = JsonCanonicalBox.canonicalize(json)
|
||||
// Accept full root (preferred)
|
||||
if json.indexOf("\"kind\":\"MIR\"") >= 0 {
|
||||
if json.indexOf("\"schema_version\":\"1.0\"") < 0 { return Result.Err("invalid schema_version") }
|
||||
if json.indexOf("\"functions\"") < 0 { return Result.Err("functions missing") }
|
||||
return Result.Ok(0)
|
||||
}
|
||||
// Tolerate narrowed JSON (single function object) used by nyvm bridge
|
||||
if json.indexOf("\"instructions\"") >= 0 && json.indexOf("\"id\"") >= 0 {
|
||||
return Result.Ok(0)
|
||||
}
|
||||
return Result.Err("invalid kind")
|
||||
}
|
||||
|
||||
// Return function JSON meta (content)
|
||||
functions(json) {
|
||||
// Canonicalize full root when present (identity until host bridge is wired)
|
||||
json = JsonCanonicalBox.canonicalize(json)
|
||||
// Provider disabled: always use scan locator
|
||||
local loc = FunctionLocatorBox.locate(json)
|
||||
if loc.is_Err() { return loc }
|
||||
return loc
|
||||
}
|
||||
|
||||
// Return blocks[] meta (content without brackets)
|
||||
blocks(func_json) {
|
||||
// Provider disabled: always use scan locator
|
||||
local loc = BlocksLocatorBox.locate(func_json)
|
||||
if loc.is_Err() { return loc }
|
||||
return loc
|
||||
}
|
||||
|
||||
// Return instructions content and single flag
|
||||
instructions(block_json) {
|
||||
// Provider disabled: always use scan locator
|
||||
local loc = InstrsLocatorBox.locate(block_json)
|
||||
if loc.is_Err() { return loc }
|
||||
return loc
|
||||
}
|
||||
|
||||
// Return last terminator object (as string)
|
||||
terminator(block_json) {
|
||||
// Provider disabled: use scan locator then parse last object
|
||||
local loc = InstrsLocatorBox.locate(block_json)
|
||||
if loc.is_Err() { return loc }
|
||||
local meta = loc.as_Ok()
|
||||
local insts = BoxHelpers.map_get(meta, "content")
|
||||
if insts == "" { return Result.Err("terminator not found") }
|
||||
local single = BoxHelpers.map_get(meta, "single")
|
||||
if single != null && single == 1 { return Result.Ok(insts) }
|
||||
// use small window for large JSON (8192 bytes)
|
||||
local last = BackwardObjectScannerBox.scan_last_object_opt_window(insts, 200000, 8192)
|
||||
if last.is_Err() { return Result.Err("terminator scan failed: " + last.as_Err()) }
|
||||
return Result.Ok(last.as_Ok())
|
||||
}
|
||||
|
||||
// Strict validate for function JSON (narrow): terminator required and references valid
|
||||
validate_function(func_json) {
|
||||
// collect block ids
|
||||
local bl = me.blocks(func_json)
|
||||
if bl.is_Err() { return bl }
|
||||
local meta = bl.as_Ok()
|
||||
local content = BoxHelpers.map_get(meta, "content")
|
||||
local ids = new MapBox()
|
||||
// iterate blocks
|
||||
local pos = 0
|
||||
loop(true) {
|
||||
local it = BlockIteratorBox.next(content, pos)
|
||||
if it.is_Err() { break }
|
||||
local m = it.as_Ok()
|
||||
local obj = BoxHelpers.map_get(m, "obj")
|
||||
pos = BoxHelpers.map_get(m, "next_pos")
|
||||
// parse id
|
||||
local key_id = "\"id\":"
|
||||
local p = obj.indexOf(key_id)
|
||||
if p < 0 { return Result.Err("block id missing") }
|
||||
p = p + key_id.size()
|
||||
// skip ws
|
||||
loop(p < obj.size()) { local ch = obj.substring(p,p+1) if ch == " " || ch == "\n" || ch == "\r" || ch == "\t" { p = p + 1 continue } break }
|
||||
local digs = StringHelpers.read_digits(obj, p)
|
||||
if digs == "" { return Result.Err("invalid block id") }
|
||||
ids.set(StringHelpers.int_to_str(StringHelpers.to_i64(digs)), 1)
|
||||
// require terminator
|
||||
if obj.indexOf("\"terminator\":{") < 0 { return Result.Err("terminator missing") }
|
||||
}
|
||||
// validate each block terminator references
|
||||
pos = 0
|
||||
loop(true) {
|
||||
local it = BlockIteratorBox.next(content, pos)
|
||||
if it.is_Err() { break }
|
||||
local m = it.as_Ok()
|
||||
pos = BoxHelpers.map_get(m, "next_pos")
|
||||
local obj = BoxHelpers.map_get(m, "obj")
|
||||
// extract terminator object
|
||||
local ts = obj.indexOf("\"terminator\":{")
|
||||
if ts < 0 { return Result.Err("terminator missing") }
|
||||
local te = ts + "\"terminator\":".size()
|
||||
// naive: use general locator to get terminator via instructions fallback if needed
|
||||
local term = me.terminator(obj)
|
||||
if term.is_Err() { return Result.Err("terminator parse failed") }
|
||||
local tj = term.as_Ok()
|
||||
// op tolerant
|
||||
local k = "\"op\""
|
||||
local p2 = tj.indexOf(k)
|
||||
if p2 < 0 { return Result.Err("terminator op not found") }
|
||||
p2 = p2 + k.size()
|
||||
loop(p2 < tj.size()) { local ch2 = tj.substring(p2,p2+1) if ch2 == ":" { p2 = p2 + 1 break } if ch2 == " " || ch2 == "\n" || ch2 == "\r" || ch2 == "\t" { p2 = p2 + 1 continue } return Result.Err("terminator op colon not found") }
|
||||
loop(p2 < tj.size()) { local ch3 = tj.substring(p2,p2+1) if ch3 == " " || ch3 == "\n" || ch3 == "\r" || ch3 == "\t" { p2 = p2 + 1 continue } break }
|
||||
if tj.substring(p2,p2+1) != "\"" { return Result.Err("terminator op quote not found") }
|
||||
local e2 = JsonCursorBox.index_of_from(tj, "\"", p2+1)
|
||||
if e2 < 0 { return Result.Err("terminator op end not found") }
|
||||
local op = tj.substring(p2+1, e2)
|
||||
if op == "jump" {
|
||||
local kt = "\"target\":"
|
||||
local pt = tj.indexOf(kt)
|
||||
if pt < 0 { return Result.Err("jump: target missing") }
|
||||
pt = pt + kt.size()
|
||||
loop(pt < tj.size()) {
|
||||
local ch = tj.substring(pt,pt+1)
|
||||
if ch == " " || ch == "\n" || ch == "\r" || ch == "\t" { pt = pt + 1 continue }
|
||||
break
|
||||
}
|
||||
local digs2 = StringHelpers.read_digits(tj, pt)
|
||||
if digs2 == "" { return Result.Err("jump: invalid target") }
|
||||
local key = StringHelpers.int_to_str(StringHelpers.to_i64(digs2))
|
||||
if call("MapBox.get/2", ids, key) == null { return Result.Err("jump: unknown target") }
|
||||
} else { if op == "branch" {
|
||||
local k1 = "\"then_bb\":"
|
||||
local p3 = tj.indexOf(k1)
|
||||
if p3 < 0 { return Result.Err("branch: then_bb missing") }
|
||||
p3 = p3 + k1.size()
|
||||
loop(p3 < tj.size()) { local ch = tj.substring(p3,p3+1) if ch == " " || ch == "\n" || ch == "\r" || ch == "\t" { p3 = p3 + 1 continue } break }
|
||||
local d1 = StringHelpers.read_digits(tj, p3)
|
||||
if d1 == "" { return Result.Err("branch: invalid then_bb") }
|
||||
local k2 = "\"else_bb\":"
|
||||
local p4 = tj.indexOf(k2)
|
||||
if p4 < 0 { return Result.Err("branch: else_bb missing") }
|
||||
p4 = p4 + k2.size()
|
||||
loop(p4 < tj.size()) { local ch = tj.substring(p4,p4+1) if ch == " " || ch == "\n" || ch == "\r" || ch == "\t" { p4 = p4 + 1 continue } break }
|
||||
local d2 = StringHelpers.read_digits(tj, p4)
|
||||
if d2 == "" { return Result.Err("branch: invalid else_bb") }
|
||||
if call("MapBox.get/2", ids, StringHelpers.int_to_str(StringHelpers.to_i64(d1))) == null { return Result.Err("branch: unknown then_bb") }
|
||||
if call("MapBox.get/2", ids, StringHelpers.int_to_str(StringHelpers.to_i64(d2))) == null { return Result.Err("branch: unknown else_bb") }
|
||||
} else { if op == "ret" { /* ok */ } else { return Result.Err("terminator: unsupported op "" + op + """) } }
|
||||
}
|
||||
}
|
||||
return Result.Ok(0)
|
||||
}
|
||||
}
|
||||
243
lang/src/shared/mir/mir_schema_box.hako
Normal file
243
lang/src/shared/mir/mir_schema_box.hako
Normal file
@ -0,0 +1,243 @@
|
||||
// selfhost/shared/mir/mir_schema_box.hako
|
||||
// MirSchemaBox — minimal MIR(JSON v0) constructors (P1 scope)
|
||||
|
||||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||||
|
||||
static box MirSchemaBox {
|
||||
_expect_map(val, context) {
|
||||
if val == null {
|
||||
print("[MirSchemaBox] dev assert failed: expected MapBox (non-null) for " + context)
|
||||
call("MapBox.get/2", val, "__mir_schema_expect_map_null")
|
||||
return val
|
||||
}
|
||||
local repr = "" + val
|
||||
if repr.indexOf("MapBox(") == 0 { return val }
|
||||
print("[MirSchemaBox] dev assert failed: expected MapBox for " + context)
|
||||
call("MapBox.get/2", val, "__mir_schema_expect_map")
|
||||
return val
|
||||
}
|
||||
_expect_array(val, context) {
|
||||
if val == null {
|
||||
print("[MirSchemaBox] dev assert failed: expected ArrayBox (non-null) for " + context)
|
||||
call("ArrayBox.get/2", val, 0)
|
||||
return val
|
||||
}
|
||||
local repr = "" + val
|
||||
if repr.indexOf("ArrayBox(") == 0 { return val }
|
||||
print("[MirSchemaBox] dev assert failed: expected ArrayBox for " + context)
|
||||
call("ArrayBox.get/2", val, 0)
|
||||
return val
|
||||
}
|
||||
_expect_i64(val, context) {
|
||||
if val == null {
|
||||
print("[MirSchemaBox] dev assert failed: expected i64 (non-null) for " + context)
|
||||
call("MapBox.get/2", val, "__mir_schema_expect_i64_null")
|
||||
return 0
|
||||
}
|
||||
local repr = "" + val
|
||||
if repr.indexOf("MapBox(") == 0 {
|
||||
local ty = call("MapBox.get/2", val, "type")
|
||||
if ty != null {
|
||||
local ty_str = "" + ty
|
||||
if ty_str != "i64" && ty_str != "int" && ty_str != "integer" {
|
||||
print("[MirSchemaBox] dev assert failed: unexpected type " + ty_str + " in " + context)
|
||||
call("MapBox.get/2", val, "__mir_schema_expect_i64_type")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
local inner = call("MapBox.get/2", val, "value")
|
||||
if inner != null { return StringHelpers.to_i64(inner) }
|
||||
print("[MirSchemaBox] dev assert failed: missing value in " + context)
|
||||
call("MapBox.get/2", val, "__mir_schema_expect_i64_value")
|
||||
return 0
|
||||
}
|
||||
if StringHelpers.is_numeric_str("" + val) == 1 { return StringHelpers.to_i64(val) }
|
||||
print("[MirSchemaBox] dev assert failed: expected numeric value for " + context)
|
||||
call("MapBox.get/2", val, "__mir_schema_expect_i64_direct")
|
||||
return 0
|
||||
}
|
||||
_len(arr) {
|
||||
if arr == null { return 0 }
|
||||
// ArrayBox.size/1 は MapBox-wrapped integer を返すので、unwrap する
|
||||
local size_val = call("ArrayBox.size/1", arr)
|
||||
local repr = "" + size_val
|
||||
if repr.indexOf("MapBox(") == 0 {
|
||||
local inner = call("MapBox.get/2", size_val, "value")
|
||||
if inner != null { return inner }
|
||||
}
|
||||
return size_val
|
||||
}
|
||||
// Scalars (normalize to i64 for P1)
|
||||
i(x) {
|
||||
// Return MapBox { type: "i64", value: x }
|
||||
local m = new MapBox()
|
||||
m.set("type", "i64")
|
||||
m.set("value", x)
|
||||
return m
|
||||
}
|
||||
|
||||
// Instructions
|
||||
inst_const(dst, val) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "const")
|
||||
m.set("dst", this.i(dst))
|
||||
m.set("value", this.i(val))
|
||||
return m
|
||||
}
|
||||
inst_ret(val) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "ret")
|
||||
m.set("value", this.i(val))
|
||||
return m
|
||||
}
|
||||
inst_compare(cmp, lhs, rhs, dst) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "compare")
|
||||
m.set("cmp", cmp)
|
||||
m.set("lhs", this.i(lhs))
|
||||
m.set("rhs", this.i(rhs))
|
||||
m.set("dst", this.i(dst))
|
||||
return m
|
||||
}
|
||||
inst_binop(kind, lhs, rhs, dst) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "binop")
|
||||
m.set("op_kind", kind)
|
||||
m.set("lhs", this.i(lhs))
|
||||
m.set("rhs", this.i(rhs))
|
||||
m.set("dst", this.i(dst))
|
||||
return m
|
||||
}
|
||||
inst_branch(cond, then_id, else_id) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "branch")
|
||||
m.set("cond", this.i(cond))
|
||||
m.set("then", this.i(then_id))
|
||||
m.set("else", this.i(else_id))
|
||||
return m
|
||||
}
|
||||
inst_jump(target) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "jump")
|
||||
m.set("target", this.i(target))
|
||||
return m
|
||||
}
|
||||
|
||||
// Phi instruction (v0 schema)
|
||||
phi_incoming(block_id, value_id) {
|
||||
local m = new MapBox()
|
||||
m.set("block", this.i(block_id))
|
||||
m.set("value", this.i(value_id))
|
||||
return m
|
||||
}
|
||||
inst_phi(dst, incoming) {
|
||||
local m = new MapBox()
|
||||
m.set("op", "phi")
|
||||
m.set("dst", this.i(dst))
|
||||
local arr = new ArrayBox()
|
||||
if incoming != null {
|
||||
local n = me._len(incoming)
|
||||
local i = 0
|
||||
loop(i < n) {
|
||||
arr.push(call("ArrayBox.get/2", incoming, i))
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
m.set("values", arr)
|
||||
return m
|
||||
}
|
||||
|
||||
// Unified call (mir_call) variants — minimal P3
|
||||
_wrap_ids(arr) {
|
||||
local out = new ArrayBox()
|
||||
if arr == null { return out }
|
||||
me._expect_array(arr, "mir_call args source")
|
||||
local i = 0
|
||||
local n = me._len(arr)
|
||||
loop(i < n) {
|
||||
out.push(this.i(call("ArrayBox.get/2", arr, i)))
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
inst_mir_call_extern(name, arg_ids, dst) {
|
||||
local callee = new MapBox(); callee.set("type", "Extern"); callee.set("name", name)
|
||||
local payload = new MapBox()
|
||||
payload.set("callee", callee)
|
||||
payload.set("args", this._wrap_ids(arg_ids))
|
||||
payload.set("effects", new ArrayBox())
|
||||
local m = new MapBox()
|
||||
m.set("op", "mir_call")
|
||||
m.set("dst", this.i(dst))
|
||||
me._expect_map(payload, "mir_call payload (extern)")
|
||||
m.set("mir_call", payload)
|
||||
return m
|
||||
}
|
||||
inst_mir_call_global(name, arg_ids, dst) {
|
||||
local callee = new MapBox(); callee.set("type", "Global"); callee.set("name", name)
|
||||
local payload = new MapBox()
|
||||
payload.set("callee", callee)
|
||||
payload.set("args", this._wrap_ids(arg_ids))
|
||||
payload.set("effects", new ArrayBox())
|
||||
local m = new MapBox()
|
||||
m.set("op", "mir_call")
|
||||
m.set("dst", this.i(dst))
|
||||
me._expect_map(payload, "mir_call payload (global)")
|
||||
m.set("mir_call", payload)
|
||||
return m
|
||||
}
|
||||
inst_mir_call_method(method, recv_id, arg_ids, dst) {
|
||||
local callee = new MapBox()
|
||||
callee.set("type", "Method")
|
||||
callee.set("method", method)
|
||||
callee.set("receiver", this.i(recv_id))
|
||||
local m = new MapBox()
|
||||
m.set("op", "mir_call")
|
||||
m.set("dst", this.i(dst))
|
||||
local payload = new MapBox()
|
||||
payload.set("callee", callee)
|
||||
payload.set("args", this._wrap_ids(arg_ids))
|
||||
payload.set("effects", new ArrayBox())
|
||||
payload = me._expect_map(payload, "mir_call payload (method)")
|
||||
m.set("mir_call", payload)
|
||||
return m
|
||||
}
|
||||
inst_mir_call_constructor(box_type, arg_ids, dst) {
|
||||
local callee = new MapBox(); callee.set("type", "Constructor"); callee.set("box_type", box_type)
|
||||
local m = new MapBox()
|
||||
m.set("op", "mir_call")
|
||||
m.set("dst", this.i(dst))
|
||||
local payload = new MapBox()
|
||||
payload.set("callee", callee)
|
||||
payload.set("args", this._wrap_ids(arg_ids))
|
||||
payload.set("effects", new ArrayBox())
|
||||
me._expect_map(payload, "mir_call payload (constructor)")
|
||||
m.set("mir_call", payload)
|
||||
return m
|
||||
}
|
||||
|
||||
// Block/Module
|
||||
block(id, insts) {
|
||||
local m = new MapBox()
|
||||
m.set("id", this.i(id))
|
||||
m.set("instructions", insts)
|
||||
return m
|
||||
}
|
||||
fn_main(blocks) {
|
||||
local m = new MapBox()
|
||||
m.set("name", "main")
|
||||
m.set("blocks", blocks)
|
||||
return m
|
||||
}
|
||||
module(fn_main) {
|
||||
local m = new MapBox()
|
||||
m.set("version", this.i(0))
|
||||
m.set("kind", "MIR")
|
||||
// Avoid push() to sidestep host-slot gating; build as literal for determinism
|
||||
local funcs = [ fn_main ]
|
||||
m.set("functions", funcs)
|
||||
// Fallback access path for environments where Array.size/1 is unreliable
|
||||
m.set("functions_0", fn_main)
|
||||
return m
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user