- plugin_loader_v2: store per-Box resolve() from TypeBox FFI; add resolve_method_id() and use in invoke_instance_method
- plugin_loader_unified: resolve_method() falls back to loader’s resolve when TOML lacks method entries
- nyash.toml: register JsonDocBox/JsonNodeBox methods (birth/parse/root/error; kind/get/size/at/str/int/bool)
- plugins/nyash-json-plugin:
* serde/yyjson provider switch via env NYASH_JSON_PROVIDER (default serde)
* vendored yyjson (c/yyjson) + shim; parse/root/get/size/at/str/int/bool implemented for yyjson
* TLV void returns aligned to tag=9
- PyVM: add minimal JsonDocBox/JsonNodeBox shims in ops_box.py (for dev path)
- tests/smokes: add jsonbox_{parse_ok,parse_err,nested,collect_prints}; wire two into min-gate CI
- tools: collect_prints_mixed now uses JSON-based app
- MiniVmPrints: move BinaryOp and fallback heuristics behind a dev toggle (default OFF)
- CURRENT_TASK.md: updated with provider policy and fallback stance
231 lines
9.9 KiB
Plaintext
231 lines
9.9 KiB
Plaintext
using selfhost.vm.scan as MiniVmScan
|
||
using selfhost.vm.binop as MiniVmBinOp
|
||
using selfhost.vm.compare as MiniVmCompare
|
||
// Use the JSON adapter facade for cursor ops (next_non_ws, digits)
|
||
using selfhost.vm.json as MiniJsonLoader
|
||
|
||
static box MiniVmPrints {
|
||
// dev trace flag (0=OFF)
|
||
_trace_enabled() { return 0 }
|
||
// fallback toggle for legacy heuristics (0=OFF, 1=ON)
|
||
_fallback_enabled() { return 0 }
|
||
// literal string within Print
|
||
try_print_string_value_at(json, end, print_pos) {
|
||
local scan = new MiniVmScan()
|
||
local k_val = "\"value\":\""
|
||
local s = scan.index_of_from(json, k_val, print_pos)
|
||
if s < 0 || s >= end { return -1 }
|
||
local i = s + k_val.length()
|
||
local j = scan.index_of_from(json, "\"", i)
|
||
if j <= 0 || j > end { return -1 }
|
||
print(json.substring(i, j))
|
||
return j + 1
|
||
}
|
||
|
||
// literal int within Print (typed)
|
||
try_print_int_value_at(json, end, print_pos) {
|
||
local scan = new MiniVmScan()
|
||
local k_expr = "\"expression\":{"
|
||
local epos = scan.index_of_from(json, k_expr, print_pos)
|
||
if epos <= 0 || epos >= end { return -1 }
|
||
local obj_start = scan.index_of_from(json, "{", epos)
|
||
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 }
|
||
// robust: look for explicit int type within expression object
|
||
local k_tint = "\"type\":\"int\""
|
||
local tpos = scan.index_of_from(json, k_tint, obj_start)
|
||
if tpos <= 0 || tpos >= obj_end { return -1 }
|
||
local k_val2 = "\"value\":"
|
||
local v2 = scan.index_of_from(json, k_val2, tpos)
|
||
if v2 <= 0 || v2 >= obj_end { return -1 }
|
||
local digits = scan.read_digits(json, v2 + k_val2.length())
|
||
if digits == "" { return -1 }
|
||
print(digits)
|
||
return obj_end + 1
|
||
}
|
||
|
||
// minimal FunctionCall printer for echo/itoa
|
||
try_print_functioncall_at(json, end, print_pos) {
|
||
local scan = new MiniVmScan()
|
||
local k_fc = "\"kind\":\"FunctionCall\""
|
||
local fcp = scan.index_of_from(json, k_fc, print_pos)
|
||
if fcp <= 0 || fcp >= end { return -1 }
|
||
local k_name = "\"name\":\""
|
||
local npos = scan.index_of_from(json, k_name, fcp)
|
||
if npos <= 0 || npos >= end { return -1 }
|
||
local ni = npos + k_name.length()
|
||
local nj = scan.index_of_from(json, "\"", ni)
|
||
if nj <= 0 || nj > end { return -1 }
|
||
local fname = json.substring(ni, nj)
|
||
local k_args = "\"arguments\":["
|
||
local apos = scan.index_of_from(json, k_args, nj)
|
||
if apos <= 0 || apos >= end { return -1 }
|
||
local arr_start = scan.index_of_from(json, "[", apos)
|
||
local arr_end = scan.find_balanced_array_end(json, arr_start)
|
||
if arr_start <= 0 || arr_end <= 0 || arr_end > end { return -1 }
|
||
// handle empty args []
|
||
local nn = new MiniJsonLoader().next_non_ws(json, arr_start+1)
|
||
if nn > 0 && nn <= arr_end {
|
||
if json.substring(nn, nn+1) == "]" {
|
||
if fname == "echo" { print("") return arr_end + 1 }
|
||
if fname == "itoa" { print("0") return arr_end + 1 }
|
||
return -1
|
||
}
|
||
}
|
||
// first arg type
|
||
local k_t = "\"type\":\""
|
||
local atpos = scan.index_of_from(json, k_t, arr_start)
|
||
if atpos <= 0 || atpos >= arr_end {
|
||
if fname == "echo" { print("") return arr_end + 1 }
|
||
if fname == "itoa" { print("0") return arr_end + 1 }
|
||
return -1
|
||
}
|
||
atpos = atpos + k_t.length()
|
||
local at_end = scan.index_of_from(json, "\"", atpos)
|
||
if at_end <= 0 || at_end > arr_end { return -1 }
|
||
local aty = json.substring(atpos, at_end)
|
||
if aty == "string" {
|
||
local k_sval = "\"value\":\""
|
||
local svalp = scan.index_of_from(json, k_sval, at_end)
|
||
if svalp <= 0 || svalp >= arr_end { return -1 }
|
||
local si = svalp + k_sval.length()
|
||
local sj = scan.index_of_from(json, "\"", si)
|
||
if sj <= 0 || sj > arr_end { return -1 }
|
||
local sval = json.substring(si, sj)
|
||
if fname == "echo" { print(sval) return sj + 1 }
|
||
return -1
|
||
}
|
||
if aty == "int" || aty == "i64" || aty == "integer" {
|
||
local k_ival = "\"value\":"
|
||
local ivalp = scan.index_of_from(json, k_ival, at_end)
|
||
if ivalp <= 0 || ivalp >= arr_end { return -1 }
|
||
local digits = scan.read_digits(json, ivalp + k_ival.length())
|
||
if fname == "itoa" || fname == "echo" { print(digits) return ivalp + k_ival.length() }
|
||
return -1
|
||
}
|
||
return -1
|
||
}
|
||
// Print all Print-Literal values within [start,end]
|
||
print_prints_in_slice(json, start, end) {
|
||
local scan = new MiniVmScan()
|
||
local bin = new MiniVmBinOp()
|
||
local cmp = new MiniVmCompare()
|
||
local pos = start
|
||
local printed = 0
|
||
local guard = 0
|
||
local trace = _trace_enabled()
|
||
loop (true) {
|
||
guard = guard + 1
|
||
if guard > 200 { break }
|
||
local k_print = "\"kind\":\"Print\""
|
||
local p = scan.index_of_from(json, k_print, pos)
|
||
if p < 0 || p > end { break }
|
||
// bound current Print object
|
||
local p_obj_start = scan.index_of_from(json, "{", p)
|
||
local p_obj_end = scan.find_balanced_object_end(json, p_obj_start)
|
||
if p_obj_start <= 0 || p_obj_end <= 0 { p_obj_end = p + k_print.length() }
|
||
// also compute coarse slice end by next Print marker to guard when object balance is not reliable
|
||
local next_p = scan.index_of_from(json, k_print, p + k_print.length())
|
||
local p_slice_end = end
|
||
if next_p > 0 { p_slice_end = next_p }
|
||
// 1) BinaryOp fallbacks(開発用トグル。既定OFF)
|
||
if (new MiniVmPrints()._fallback_enabled() == 1) {
|
||
local nextp = bin.try_print_binop_sum_any(json, end, p)
|
||
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
nextp = bin.try_print_binop_at(json, end, p)
|
||
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
nextp = bin.try_print_binop_int_greedy(json, end, p)
|
||
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
nextp = bin.try_print_binop_sum_any(json, end, p)
|
||
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
// Inline typed sum within this Print BinaryOp('+')
|
||
{
|
||
local k_expr = "\"expression\":{"
|
||
local epos = scan.index_of_from(json, k_expr, p)
|
||
if epos > 0 { if epos < p_obj_end {
|
||
if scan.index_of_from(json, "\"kind\":\"BinaryOp\"", epos) > 0 {
|
||
if scan.index_of_from(json, "\"operator\":\"+\"", epos) > 0 {
|
||
local k_l = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||
local k_r = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||
local lp = scan.index_of_from(json, k_l, epos)
|
||
if lp > 0 { if lp < p_obj_end {
|
||
local ld = scan.read_digits(json, lp + k_l.length())
|
||
if ld != "" {
|
||
local rp = scan.index_of_from(json, k_r, lp + k_l.length())
|
||
if rp > 0 { if rp < p_obj_end {
|
||
local rd = scan.read_digits(json, rp + k_r.length())
|
||
if rd != "" { print(new MiniVmScan()._int_to_str(new MiniVmScan()._str_to_int(ld) + new MiniVmScan()._str_to_int(rd))) printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
}}
|
||
}
|
||
}}
|
||
}
|
||
}
|
||
}}
|
||
}
|
||
}
|
||
// 2) Compare
|
||
nextp = cmp.try_print_compare_at(json, end, p)
|
||
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
// 3) FunctionCall minimal
|
||
nextp = new MiniVmPrints().try_print_functioncall_at(json, end, p)
|
||
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
// 4) literal string
|
||
nextp = new MiniVmPrints().try_print_string_value_at(json, end, p)
|
||
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
// 5) literal int via type
|
||
nextp = new MiniVmPrints().try_print_int_value_at(json, end, p)
|
||
if nextp > 0 { printed = printed + 1 pos = p_obj_end + 1 continue }
|
||
// 5b) literal int(簡易フォールバック;既定OFF)
|
||
if (new MiniVmPrints()._fallback_enabled() == 1) {
|
||
local ki = "\"type\":\"int\",\"value\":"
|
||
local pi = scan.index_of_from(json, ki, p)
|
||
if pi > 0 { if pi < p_slice_end {
|
||
local digits = scan.read_digits(json, pi + ki.length())
|
||
if digits != "" { print(digits) printed = printed + 1 pos = p_slice_end continue }
|
||
}}
|
||
// Unknown shape: skip this Print object entirely to avoid stalls
|
||
pos = p_obj_end + 1
|
||
if pos <= p { pos = p + k_print.length() }
|
||
} else {
|
||
// 既定は最小前進(次の探索へ)
|
||
pos = p + k_print.length()
|
||
}
|
||
}
|
||
return printed
|
||
}
|
||
|
||
// Process top-level If with literal condition; print branch prints. Returns printed count.
|
||
process_if_once(json) {
|
||
local scan = new MiniVmScan()
|
||
local k_if = "\"kind\":\"If\""
|
||
local p = scan.index_of_from(json, k_if, 0)
|
||
if p < 0 { return 0 }
|
||
local k_cond = "\"condition\""
|
||
local cpos = scan.index_of_from(json, k_cond, p)
|
||
if cpos < 0 { return 0 }
|
||
local k_val = "\"value\":"
|
||
local vpos = scan.index_of_from(json, k_val, cpos)
|
||
if vpos < 0 { return 0 }
|
||
local val_digits = scan.read_digits(json, vpos + k_val.length())
|
||
local truthy = 0
|
||
if val_digits { if val_digits != "0" { truthy = 1 } }
|
||
local k_then = "\"then_body\""
|
||
local k_else = "\"else_body\""
|
||
local bkey = k_then
|
||
if truthy == 0 { bkey = k_else }
|
||
local bpos = scan.index_of_from(json, bkey, cpos)
|
||
if bpos < 0 { return 0 }
|
||
local arr_start = scan.index_of_from(json, "[", bpos)
|
||
if arr_start < 0 { return 0 }
|
||
local arr_end = new MiniVmScan().find_balanced_array_end(json, arr_start)
|
||
if arr_end < 0 { return 0 }
|
||
return new MiniVmPrints().print_prints_in_slice(json, arr_start, arr_end)
|
||
}
|
||
|
||
// Print all Print-Literal values in Program.statements (string/int only; MVP)
|
||
print_all_print_literals(json) {
|
||
return new MiniVmPrints().print_prints_in_slice(json, 0, json.length())
|
||
}
|
||
}
|