2025-10-31 20:45:46 +09:00
|
|
|
|
// json_frag.hako — JSON v0 断片抽出ユーティリティ(Box)
|
|
|
|
|
|
// 責務: 文字列JSONから key:int / key:str を簡便に取り出す。
|
|
|
|
|
|
// 非責務: 実行・評価(構造検査やVM実行は他箱に委譲)。
|
|
|
|
|
|
|
2025-11-02 04:13:17 +09:00
|
|
|
|
using selfhost.shared.json.core.json_cursor as JsonCursorBox
|
|
|
|
|
|
using selfhost.shared.common.string_helpers as StringHelpers
|
2025-10-31 20:45:46 +09:00
|
|
|
|
|
|
|
|
|
|
static box JsonFragBox {
|
2025-11-03 16:09:19 +09:00
|
|
|
|
// 基本ヘルパ - VM fallback implementations for cross-box static calls
|
|
|
|
|
|
index_of_from(hay, needle, pos) {
|
|
|
|
|
|
// VM fallback: implement using substring + indexOf
|
|
|
|
|
|
if hay == null || needle == null { return -1 }
|
|
|
|
|
|
local s = "" + hay
|
|
|
|
|
|
local n = s.length()
|
|
|
|
|
|
local p2 = pos
|
|
|
|
|
|
if p2 < 0 { p2 = 0 }
|
|
|
|
|
|
if p2 >= n { return -1 }
|
|
|
|
|
|
// Extract substring from pos onwards
|
|
|
|
|
|
local substr = s.substring(p2, n)
|
|
|
|
|
|
// Find needle in substring
|
|
|
|
|
|
local idx = substr.indexOf(needle)
|
|
|
|
|
|
if idx < 0 { return -1 }
|
|
|
|
|
|
return p2 + idx
|
|
|
|
|
|
}
|
2025-10-31 20:45:46 +09:00
|
|
|
|
read_digits(text, pos) { return StringHelpers.read_digits(text, pos) }
|
|
|
|
|
|
_str_to_int(s) { return StringHelpers.to_i64(s) }
|
2025-11-03 16:09:19 +09:00
|
|
|
|
_to_bool10(ch) { if ch == "t" { return 1 } if ch == "f" { return 0 } return null }
|
|
|
|
|
|
|
|
|
|
|
|
// Read helpers (pos-based)
|
|
|
|
|
|
read_int_from(text, pos) {
|
|
|
|
|
|
if text == null { return null }
|
|
|
|
|
|
local s = "" + text
|
|
|
|
|
|
local i = pos
|
|
|
|
|
|
local n = s.length()
|
|
|
|
|
|
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
|
|
|
|
|
local j = i
|
|
|
|
|
|
if j < n && (s.substring(j,j+1) == "-" || s.substring(j,j+1) == "+") { j = j + 1 }
|
|
|
|
|
|
local had = 0
|
|
|
|
|
|
loop(j < n) {
|
|
|
|
|
|
local ch = s.substring(j,j+1)
|
|
|
|
|
|
if ch >= "0" && ch <= "9" { had = 1 j = j + 1 } else { break }
|
|
|
|
|
|
}
|
|
|
|
|
|
if had == 0 { return null }
|
|
|
|
|
|
return s.substring(i, j)
|
|
|
|
|
|
}
|
|
|
|
|
|
read_bool_from(text, pos) {
|
|
|
|
|
|
if text == null { return null }
|
|
|
|
|
|
local s = "" + text
|
|
|
|
|
|
local i = pos
|
|
|
|
|
|
local n = s.length()
|
|
|
|
|
|
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
|
|
|
|
|
if i < n { return me._to_bool10(s.substring(i,i+1)) }
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
read_string_from(text, pos) {
|
|
|
|
|
|
if text == null { return null }
|
|
|
|
|
|
local s = "" + text
|
|
|
|
|
|
local i = pos
|
|
|
|
|
|
local n = s.length()
|
|
|
|
|
|
// Find opening quote
|
|
|
|
|
|
loop(i < n) { if s.substring(i,i+1) == "\"" { i = i + 1 break } if s.substring(i,i+1) != " " { break } i = i + 1 }
|
|
|
|
|
|
local j = i
|
|
|
|
|
|
loop(j < n) { if s.substring(j,j+1) == "\"" { break } j = j + 1 }
|
|
|
|
|
|
if j <= i { return null }
|
|
|
|
|
|
return s.substring(i, j)
|
|
|
|
|
|
}
|
|
|
|
|
|
read_float_from(text, pos) {
|
|
|
|
|
|
if text == null { return null }
|
|
|
|
|
|
local s = "" + text
|
|
|
|
|
|
local i = pos
|
|
|
|
|
|
local n = s.length()
|
|
|
|
|
|
loop(i < n) { if s.substring(i,i+1) != " " { break } i = i + 1 }
|
|
|
|
|
|
local j = i
|
|
|
|
|
|
if j < n && (s.substring(j,j+1) == "+" || s.substring(j,j+1) == "-") { j = j + 1 }
|
|
|
|
|
|
local had = 0
|
|
|
|
|
|
loop(j < n) {
|
|
|
|
|
|
local ch = s.substring(j,j+1)
|
|
|
|
|
|
if (ch >= "0" && ch <= "9") || ch == "." { had = 1 j = j + 1 } else { break }
|
|
|
|
|
|
}
|
|
|
|
|
|
if had == 0 { return null }
|
|
|
|
|
|
return s.substring(i, j)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Read helpers (key-based, start at keyPos)
|
|
|
|
|
|
read_int_after(text, key_pos) { return me.read_int_from(text, key_pos) }
|
|
|
|
|
|
read_bool_after(text, key_pos) { return me.read_bool_from(text, key_pos) }
|
|
|
|
|
|
read_string_after(text, key_pos) { return me.read_string_from(text, key_pos) }
|
|
|
|
|
|
read_float_after(text, key_pos) { return me.read_float_from(text, key_pos) }
|
2025-10-31 20:45:46 +09:00
|
|
|
|
|
|
|
|
|
|
// key に続く数値(最初の一致)を返す。見つからなければ null。
|
|
|
|
|
|
get_int(seg, key) {
|
|
|
|
|
|
local pat1 = "\"" + key + "\":"
|
|
|
|
|
|
local p = me.index_of_from(seg, pat1, 0)
|
|
|
|
|
|
if p >= 0 {
|
2025-11-03 23:21:48 +09:00
|
|
|
|
// tolerant: skip whitespace and optional sign
|
|
|
|
|
|
local v = me.read_int_after(seg, p + pat1.length())
|
|
|
|
|
|
if v != null { return me._str_to_int(v) }
|
2025-10-31 20:45:46 +09:00
|
|
|
|
}
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-03 16:09:19 +09:00
|
|
|
|
// Scan for closing quote (VM fallback for scan_string_end)
|
|
|
|
|
|
_scan_string_end(text, quote_pos) {
|
|
|
|
|
|
// quote_pos is the position of opening quote
|
|
|
|
|
|
// Return position of closing quote, or -1 if not found
|
|
|
|
|
|
if text == null { return -1 }
|
|
|
|
|
|
local s = "" + text
|
|
|
|
|
|
local n = s.length()
|
|
|
|
|
|
local i = quote_pos + 1
|
|
|
|
|
|
loop(i < n) {
|
|
|
|
|
|
local ch = s.substring(i, i+1)
|
|
|
|
|
|
if ch == "\"" { return i }
|
|
|
|
|
|
if ch == "\\" {
|
|
|
|
|
|
i = i + 1 // Skip escaped character
|
|
|
|
|
|
if i >= n { return -1 }
|
|
|
|
|
|
}
|
|
|
|
|
|
i = i + 1
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Seek matching closing bracket (VM fallback for seek_array_end)
|
|
|
|
|
|
_seek_array_end(text, lbracket_pos) {
|
|
|
|
|
|
// lbracket_pos is the position of '['
|
|
|
|
|
|
// Return position of matching ']', or -1 if not found
|
|
|
|
|
|
if text == null { return -1 }
|
|
|
|
|
|
local s = "" + text
|
|
|
|
|
|
local n = s.length()
|
|
|
|
|
|
local depth = 0
|
|
|
|
|
|
local i = lbracket_pos
|
|
|
|
|
|
local in_str = 0
|
|
|
|
|
|
loop(i < n) {
|
|
|
|
|
|
local ch = s.substring(i, i+1)
|
|
|
|
|
|
if in_str == 1 {
|
|
|
|
|
|
if ch == "\"" { in_str = 0 }
|
|
|
|
|
|
if ch == "\\" { i = i + 1 } // Skip escaped char
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if ch == "\"" { in_str = 1 }
|
|
|
|
|
|
if ch == "[" { depth = depth + 1 }
|
|
|
|
|
|
if ch == "]" {
|
|
|
|
|
|
depth = depth - 1
|
|
|
|
|
|
if depth == 0 { return i }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
i = i + 1
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 20:45:46 +09:00
|
|
|
|
// key に続く "..." の文字列(最初の一致)を返す。見つからなければ空文字。
|
|
|
|
|
|
get_str(seg, key) {
|
|
|
|
|
|
local pat = "\"" + key + "\":\""
|
|
|
|
|
|
local p = me.index_of_from(seg, pat, 0)
|
|
|
|
|
|
if p >= 0 {
|
2025-11-01 13:28:56 +09:00
|
|
|
|
local vstart = p + pat.length() // start of value (right after opening quote)
|
2025-11-03 16:09:19 +09:00
|
|
|
|
local vend = me._scan_string_end(seg, vstart - 1)
|
2025-10-31 20:45:46 +09:00
|
|
|
|
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\":["
|
2025-11-01 13:28:56 +09:00
|
|
|
|
local pk = mjson.indexOf(key)
|
2025-10-31 20:45:46 +09:00
|
|
|
|
if pk < 0 { return "" }
|
|
|
|
|
|
// '[' position
|
2025-11-01 13:28:56 +09:00
|
|
|
|
local arr_bracket = pk + key.length() - 1
|
2025-10-31 20:45:46 +09:00
|
|
|
|
// Use escape-aware scanner to find matching ']'
|
2025-11-03 16:09:19 +09:00
|
|
|
|
local endp = me._seek_array_end(mjson, arr_bracket)
|
2025-10-31 20:45:46 +09:00
|
|
|
|
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`.
|
|
|
|
|
|
}
|