Files
hakorune/apps/selfhost/vm/boxes/mini_vm_prints.nyash
Selfhosting Dev fdc889ed58 selfhost: Integrate ChatGPT's using system parser fixes
- Fix using paths in mini_vm_prints.nyash and mini_vm_core.nyash
- Convert multi-line JSON literal to single-line in empty args smoke test
- Remove extra closing brace in mini_vm_core.nyash
- Add verbose logging for using system visited file tracking

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-23 02:15:53 +09:00

532 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

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

using selfhost.common.mini_vm_scan as MiniVmScan
using selfhost.common.mini_vm_binop as MiniVmBinOp
using selfhost.common.mini_vm_compare as MiniVmCompare
// Use the JSON adapter facade for cursor ops (next_non_ws, digits)
using selfhost.vm.boxes.json_cur 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) {
// Preferred route: JSON Box (plugin) robust and structure-aware
// If plugin is available, parse and traverse Program.statements and print.
// Fall back to text scanner when plugin is unavailable or parse fails.
{
// Attempt plugin route in a guarded block
@printed = 0
@ok = 0
@dbg = _trace_enabled()
// new JsonDocBox()/JsonNodeBox are provided by the JSON plugin
@doc = new JsonDocBox()
doc.parse(json)
if dbg == 1 {
@perr = doc.error()
if perr == "" { print("[json] parse ok") } else { print("[json] parse err=" + perr) }
}
@root = doc.root()
if dbg == 1 {
@rkind = root.kind()
print("[json] root.kind=" + rkind)
}
if root {
@stmts = root.get("statements")
if dbg == 1 {
@skind = stmts.kind()
@ssize = stmts.size()
print("[json] stmts.kind=" + skind + " size=" + new MiniVmScan()._int_to_str(ssize))
}
if stmts {
@n = stmts.size()
@i = 0
loop (i < n) {
if dbg == 1 {
print("[json] loop i=" + new MiniVmScan()._int_to_str(i) + "/" + new MiniVmScan()._int_to_str(n))
if i > 1000 { print("[json] debug guard: break loop at i>1000") break }
}
@node = stmts.at(i)
if !node { i = i + 1 continue }
@expr = node.get("expression")
if !expr { i = i + 1 continue }
@k = expr.get("kind").str()
if dbg == 1 { print("[json] expr.kind=" + k) }
if k == "Literal" {
@val = expr.get("value")
if val {
@ty = val.get("type").str()
if ty == "string" { print(val.get("value").str()) } else { print(val.get("value").int()) }
}
printed = printed + 1
i = i + 1
continue
}
if k == "FunctionCall" {
@name = expr.get("name").str()
if dbg == 1 { print("[json] func name=" + name) }
@args = expr.get("arguments")
if !args { i = i + 1 continue }
@asz = args.size()
if asz <= 0 {
if name == "echo" { print("") }
if name == "itoa" { print("0") }
printed = printed + 1
i = i + 1
continue
}
@arg0v = args.at(0).get("value")
if name == "echo" {
if arg0v {
@t = arg0v.get("type").str()
if t == "string" { print(arg0v.get("value").str()) } else { print(arg0v.get("value").int()) }
}
printed = printed + 1
if dbg == 1 { print("[json] before inc i=" + new MiniVmScan()._int_to_str(i)) }
i = i + 1
if dbg == 1 { print("[json] after inc i=" + new MiniVmScan()._int_to_str(i)) }
continue
}
if name == "itoa" {
if arg0v { print(arg0v.get("value").int()) }
printed = printed + 1
if dbg == 1 { print("[json] before inc i=" + new MiniVmScan()._int_to_str(i)) }
i = i + 1
if dbg == 1 { print("[json] after inc i=" + new MiniVmScan()._int_to_str(i)) }
continue
}
printed = printed + 1
if dbg == 1 { print("[json] before inc i=" + new MiniVmScan()._int_to_str(i)) }
i = i + 1
if dbg == 1 { print("[json] after inc i=" + new MiniVmScan()._int_to_str(i)) }
continue
}
if k == "Compare" {
@op = expr.get("operation").str()
@lhs = expr.get("lhs").get("value").get("value").int()
@rhs = expr.get("rhs").get("value").get("value").int()
if op == "<" { if lhs < rhs { print(1) } else { print(0) } }
if op == "==" { if lhs == rhs { print(1) } else { print(0) } }
if op == "<=" { if lhs <= rhs { print(1) } else { print(0) } }
if op == ">" { if lhs > rhs { print(1) } else { print(0) } }
if op == ">=" { if lhs >= rhs { print(1) } else { print(0) } }
if op == "!=" { if lhs != rhs { print(1) } else { print(0) } }
printed = printed + 1
i = i + 1
continue
}
if k == "BinaryOp" {
@op = expr.get("operator").str()
if op == "+" {
@left = expr.get("left").get("value").get("value").int()
@right = expr.get("right").get("value").get("value").int()
print(left + right)
printed = printed + 1
i = i + 1
continue
}
}
// Unknown expression kind: treat as a no-op; do not count
i = i + 1
}
ok = 1
}
}
// Prefer plugin result whenever JSON route ran (ok==1). Even if printed==0,
// return early to avoid falling back to the heuristic scanner which can loop
// on malformed inputs or seam-edge cases.
if dbg == 1 { print("[json] plugin_ok=" + new MiniVmScan()._int_to_str(ok) + " printed=" + new MiniVmScan()._int_to_str(printed)) }
if ok == 1 { return printed }
}
// Fallback: text scanner開発用
if _trace_enabled() == 1 { print("[json] fallback engaged") }
local scan = new MiniVmScan()
local bin = new MiniVmBinOp()
local cmp = new MiniVmCompare()
local pos = start
local printed = 0
local guard = 0
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 (coarse): use next Print marker as slice end to avoid deep brace scan
local p_obj_start = scan.index_of_from(json, "{", p)
// coarse slice end by next Print marker
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 }
// avoid heavy find_balanced_object_end; use coarse p_slice_end-1 as object end
local p_obj_end = p_slice_end - 1
if p_obj_start <= 0 { p_obj_end = p + k_print.length() }
// Fast path: handle within [p, p_slice_end) without deep brace scans
{
local did = 0
// FunctionCall echo/itoa
local k_fc = "\"kind\":\"FunctionCall\""
local fcp = scan.index_of_from(json, k_fc, p)
if fcp > 0 { if fcp < p_slice_end {
local k_name = "\"name\":\""
local npos = scan.index_of_from(json, k_name, fcp)
if npos > 0 { if npos < p_slice_end {
local ni = npos + k_name.length()
local nj = scan.index_of_from(json, "\"", ni)
if nj > 0 { if nj <= p_slice_end {
local fname = json.substring(ni, nj)
local k_args = "\"arguments\":["
local apos = scan.index_of_from(json, k_args, nj)
if apos > 0 { if apos < p_slice_end {
// quick value-based parse first (avoid type walk)
{
local k_sval = "\"value\":\""
local vs = scan.index_of_from(json, k_sval, apos)
if vs > 0 { if vs < p_slice_end {
local si = vs + k_sval.length()
local sj = scan.index_of_from(json, "\"", si)
if sj > 0 { if sj <= p_slice_end {
local sval = json.substring(si, sj)
if fname == "echo" { print(sval) printed = printed + 1 did = 1 pos = p_slice_end continue }
}}
}}
local k_ival = "\"value\":"
local vi = scan.index_of_from(json, k_ival, apos)
if vi > 0 { if vi < p_slice_end {
local digits = scan.read_digits(json, vi + k_ival.length())
if digits != "" { if fname == "itoa" || fname == "echo" { print(digits) printed = printed + 1 did = 1 pos = p_slice_end continue } }
}}
}
// empty args
local nwn = new MiniJsonLoader().next_non_ws(json, apos + k_args.length())
if nwn == apos + k_args.length() {
if fname == "echo" { print("") printed = printed + 1 did = 1 pos = p_slice_end continue }
if fname == "itoa" { print("0") printed = printed + 1 did = 1 pos = p_slice_end continue }
}
local k_t = "\"type\":\""
local atpos = scan.index_of_from(json, k_t, apos)
if atpos > 0 { if atpos < p_slice_end {
local ati = atpos + k_t.length()
local atj = scan.index_of_from(json, "\"", ati)
if atj > 0 { if atj <= p_slice_end {
local aty = json.substring(ati, atj)
if aty == "string" {
local k_sval = "\"value\":\""
local svalp = scan.index_of_from(json, k_sval, atj)
if svalp > 0 { if svalp < p_slice_end {
local si = svalp + k_sval.length()
local sj = scan.index_of_from(json, "\"", si)
if sj > 0 { if sj <= p_slice_end {
local sval = json.substring(si, sj)
if fname == "echo" { print(sval) printed = printed + 1 did = 1 pos = p_slice_end continue }
}}
}}
}
if aty == "int" || aty == "i64" || aty == "integer" {
local k_ival = "\"value\":"
local ivalp = scan.index_of_from(json, k_ival, atj)
if ivalp > 0 { if ivalp < p_slice_end {
local digits = scan.read_digits(json, ivalp + k_ival.length())
if fname == "itoa" || fname == "echo" { print(digits) printed = printed + 1 did = 1 pos = p_slice_end continue }
}}
}
}}
}
}
}}
}}
}}
}}
// Compare within slice
local k_cp = "\"kind\":\"Compare\""
local cpos = scan.index_of_from(json, k_cp, p)
if cpos > 0 { if cpos < p_slice_end {
local k_op = "\"operation\":\""
local opos = scan.index_of_from(json, k_op, cpos)
if opos > 0 { if opos < p_slice_end {
local oi = opos + k_op.length()
local oj = scan.index_of_from(json, "\"", oi)
if oj > 0 { if oj <= p_slice_end {
local op = json.substring(oi, oj)
local k_lhs = "\"lhs\":{\"kind\":\"Literal\""
local hl = scan.index_of_from(json, k_lhs, oj)
if hl > 0 { if hl < p_slice_end {
local k_v = "\"value\":"
local hv = scan.index_of_from(json, k_v, hl)
if hv > 0 { if hv < p_slice_end {
local a = scan.read_digits(json, hv + k_v.length())
local k_rhs = "\"rhs\":{\"kind\":\"Literal\""
local hr = scan.index_of_from(json, k_rhs, hl)
if hr > 0 { if hr < p_slice_end {
local rv = scan.index_of_from(json, k_v, hr)
if rv > 0 { if rv < p_slice_end {
local b = scan.read_digits(json, rv + k_v.length())
if a && b {
local ai = scan._str_to_int(a)
local bi = scan._str_to_int(b)
local res = 0
if op == "<" { if ai < bi { res = 1 } }
if op == "==" { if ai == bi { res = 1 } }
if op == "<=" { if ai <= bi { res = 1 } }
if op == ">" { if ai > bi { res = 1 } }
if op == ">=" { if ai >= bi { res = 1 } }
if op == "!=" { if ai != bi { res = 1 } }
print(res)
printed = printed + 1
did = 1
pos = p_slice_end
continue
}
}}
}}
}}
}}
}}
}}
}}
// BinaryOp '+' (typed ints) within slice
if scan.index_of_from(json, "\"kind\":\"BinaryOp\"", p) > 0 { if scan.index_of_from(json, "\"operator\":\"+\"", p) > 0 {
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, p)
if lp > 0 { if lp < p_slice_end {
local ld = scan.read_digits(json, lp + k_lint.length())
if ld != "" {
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
if rp > 0 { if rp < p_slice_end {
local rd = scan.read_digits(json, rp + k_rint.length())
if rd != "" { print(new MiniVmScan()._int_to_str(new MiniVmScan()._str_to_int(ld) + new MiniVmScan()._str_to_int(rd))) printed = printed + 1 did = 1 pos = p_slice_end continue }
}}
}
}}
}}
// Literal string within slice
{
local k_val = "\"value\":\""
local s = scan.index_of_from(json, k_val, p)
if s > 0 { if s < p_slice_end {
local i = s + k_val.length()
local j = scan.index_of_from(json, "\"", i)
if j > 0 { if j <= p_slice_end {
print(json.substring(i, j))
printed = printed + 1
did = 1
pos = p_slice_end
continue
}}
}}
}
// Literal int within slice
{
local k_tint = "\"type\":\"int\""
local tpos = scan.index_of_from(json, k_tint, p)
if tpos > 0 { if tpos < p_slice_end {
local k_val2 = "\"value\":"
local v2 = scan.index_of_from(json, k_val2, tpos)
if v2 > 0 { if v2 < p_slice_end {
local digits = scan.read_digits(json, v2 + k_val2.length())
if digits != "" { print(digits) printed = printed + 1 did = 1 pos = p_slice_end continue }
}}
}}
}
if did == 1 { pos = p_slice_end + 1 continue }
}
// 0) BinaryOp typed/expr重スキャン回避のため無効化。必要なら下の軽量パスを使用
// 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既定: 軽量スライス限定パスで処理済み。重い版は呼ばない)
// 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())
}
}