selfhost(pyvm): MiniVmPrints – prefer JSON route early-return (ok==1) to avoid fallback loops; keep default behavior unchanged elsewhere

This commit is contained in:
Selfhosting Dev
2025-09-22 07:54:25 +09:00
parent 27568eb4a6
commit 8e4cadd349
348 changed files with 9981 additions and 30074 deletions

View File

@ -1,19 +0,0 @@
// Adapter for JSON cursor operations (extracted)
// Wraps MiniJsonCur and exposes a stable facade
using selfhost.vm.json_cur 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)
}
}

View File

@ -1,201 +0,0 @@
using selfhost.vm.json as MiniJson
using selfhost.vm.scan 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.length()
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.length())
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.length()
local rval = cur.read_quoted_from(json, ri)
if rval { print(lval + rval) return ri + rval.length() + 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.length())
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.length())
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 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 nums = []
local i = obj_start
loop (i < obj_end) {
if json.substring(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.length() 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.length())
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.length())
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.length())
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.length())
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.length())
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.length())
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.length())
if d1 == "" { return "" }
local p2 = scan.index_of_from(json, k_typed, p1 + k_typed.length())
if p2 < 0 { return "" }
local d2 = scan.read_digits(json, p2 + k_typed.length())
if d2 == "" { return "" }
return scan._int_to_str(scan._str_to_int(d1) + scan._str_to_int(d2))
}
}

View File

@ -1,47 +0,0 @@
using selfhost.vm.scan 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.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.length()
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.length())
// 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.length())
if !a || !b { return -1 }
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)
// advance after rhs object (coarsely)
return rv + 1
}
}

View File

@ -1,173 +0,0 @@
// Mini-VM scanning and numeric helpers
static box MiniVmScan {
// helper: find needle from position pos
index_of_from(hay, needle, pos) {
local tail = hay.substring(pos, hay.length())
local rel = tail.indexOf(needle)
if rel < 0 { return -1 } else { return pos + rel }
}
// helper: find balanced bracket range [ ... ] starting at idx (points to '[')
find_balanced_array_end(json, idx) {
local n = json.length()
if json.substring(idx, idx+1) != "[" { return -1 }
local depth = 0
local i = idx
loop (i < n) {
local ch = json.substring(i, i+1)
if ch == "[" { depth = depth + 1 }
if ch == "]" {
depth = depth - 1
if depth == 0 { return i }
}
i = i + 1
}
return -1
}
// helper: find balanced object range { ... } starting at idx (points to '{')
find_balanced_object_end(json, idx) {
local n = json.length()
if json.substring(idx, idx+1) != "{" { return -1 }
local depth = 0
local i = idx
loop (i < n) {
local ch = json.substring(i, i+1)
if ch == "{" { depth = depth + 1 }
if ch == "}" {
depth = depth - 1
if depth == 0 { return i }
}
i = i + 1
}
return -1
}
_str_to_int(s) {
local i = 0
local n = s.length()
local acc = 0
loop (i < n) {
local ch = s.substring(i, i+1)
if ch == "0" { acc = acc * 10 + 0 i = i + 1 continue }
if ch == "1" { acc = acc * 10 + 1 i = i + 1 continue }
if ch == "2" { acc = acc * 10 + 2 i = i + 1 continue }
if ch == "3" { acc = acc * 10 + 3 i = i + 1 continue }
if ch == "4" { acc = acc * 10 + 4 i = i + 1 continue }
if ch == "5" { acc = acc * 10 + 5 i = i + 1 continue }
if ch == "6" { acc = acc * 10 + 6 i = i + 1 continue }
if ch == "7" { acc = acc * 10 + 7 i = i + 1 continue }
if ch == "8" { acc = acc * 10 + 8 i = i + 1 continue }
if ch == "9" { acc = acc * 10 + 9 i = i + 1 continue }
break
}
return acc
}
_digit_char(d) {
if d == 0 { return "0" }
if d == 1 { return "1" }
if d == 2 { return "2" }
if d == 3 { return "3" }
if d == 4 { return "4" }
if d == 5 { return "5" }
if d == 6 { return "6" }
if d == 7 { return "7" }
if d == 8 { return "8" }
if d == 9 { return "9" }
return "0"
}
_int_to_str(n) {
if n == 0 { return "0" }
local v = n
local out = ""
loop (v > 0) {
local d = v % 10
local ch = _digit_char(d)
out = ch + out
v = v / 10
}
return out
}
// Read digit runs starting at pos
read_digits(json, pos) {
local out = ""
loop (true) {
local s = json.substring(pos, pos+1)
if s == "" { break }
if s == "0" { out = out + s pos = pos + 1 continue }
if s == "1" { out = out + s pos = pos + 1 continue }
if s == "2" { out = out + s pos = pos + 1 continue }
if s == "3" { out = out + s pos = pos + 1 continue }
if s == "4" { out = out + s pos = pos + 1 continue }
if s == "5" { out = out + s pos = pos + 1 continue }
if s == "6" { out = out + s pos = pos + 1 continue }
if s == "7" { out = out + s pos = pos + 1 continue }
if s == "8" { out = out + s pos = pos + 1 continue }
if s == "9" { out = out + s pos = pos + 1 continue }
break
}
return out
}
// Linear pass: sum all numbers outside of quotes
sum_numbers_no_quotes(json) {
local i = 0
local n = json.length()
local total = 0
loop (i < n) {
local ch = json.substring(i, i+1)
if ch == "\"" {
local j = index_of_from(json, "\"", i+1)
if j < 0 { break }
i = j + 1
continue
}
local d = read_digits(json, i)
if d { total = total + _str_to_int(d) i = i + d.length() continue }
i = i + 1
}
return _int_to_str(total)
}
// Naive: sum all digit runs anywhere
sum_all_digits_naive(json) {
local i = 0
local n = json.length()
local total = 0
loop (i < n) {
local d = read_digits(json, i)
if d { total = total + _str_to_int(d) i = i + d.length() continue }
i = i + 1
}
return _int_to_str(total)
}
// Sum first two integers outside quotes; returns string or empty
sum_first_two_numbers(json) {
local i = 0
local n = json.length()
local total = 0
local found = 0
loop (i < n) {
local ch = json.substring(i, i+1)
if ch == "\"" {
local j = index_of_from(json, "\"", i+1)
if j < 0 { break }
i = j + 1
continue
}
local d = read_digits(json, i)
if d {
total = total + _str_to_int(d)
found = found + 1
i = i + d.length()
if found >= 2 { return _int_to_str(total) }
continue
}
i = i + 1
}
return ""
}
}

View File

@ -0,0 +1,249 @@
// SeamInspector — analyze inlined code seam and duplicates
// Usage: import and call report(text) or analyze_dump_file(path)
static box SeamInspector {
// Count brace delta ignoring simple strings ("...") and escapes
_brace_delta_ignoring_strings(text, start, end) {
@i = start
@n = end
@delta = 0
loop (i < n) {
@ch = text.substring(i, i+1)
if ch == "\"" {
i = i + 1
loop (i < n) {
@c = text.substring(i, i+1)
if c == "\\" { i = i + 2 continue }
if c == "\"" { i = i + 1 break }
i = i + 1
}
continue
}
if ch == "{" { delta = delta + 1 }
if ch == "}" { delta = delta - 1 }
i = i + 1
}
return delta
}
// Find index of exact token in plain text from position
_index_of_from(h, needle, pos) {
if pos < 0 { pos = 0 }
@n = h.length()
if pos >= n { return -1 }
@m = needle.length()
if m <= 0 { return pos }
@i = pos
@limit = n - m
loop (i <= limit) {
if h.substring(i, i+m) == needle { return i }
i = i + 1
}
return -1
}
// Find matching closing brace starting at '{'
_find_balanced_object_end(text, idx) {
if text.substring(idx, idx+1) != "{" { return -1 }
@i = idx
@n = text.length()
@depth = 0
loop (i < n) {
@ch = text.substring(i, i+1)
if ch == "\"" {
i = i + 1
loop (i < n) {
@c = text.substring(i, i+1)
if c == "\\" { i = i + 2 continue }
if c == "\"" { i = i + 1 break }
i = i + 1
}
continue
}
if ch == "{" { depth = depth + 1 }
if ch == "}" { depth = depth - 1 if depth == 0 { return i } }
i = i + 1
}
return -1
}
// Scan boxes: return array of {name, start, end}
_scan_boxes(text) {
@i = 0
@n = text.length()
@res = new ArrayBox()
@tok = "static box "
loop (i < n) {
@p = _index_of_from(text, tok, i)
if p < 0 { break }
@j = p + tok.length()
// read identifier
@name = ""
loop (j < n) {
@c = text.substring(j, j+1)
// ASCII alpha-num or '_'
if c == "_" { name = name + c j = j + 1 continue }
// digits
if c == "0" or c == "1" or c == "2" or c == "3" or c == "4" or c == "5" or c == "6" or c == "7" or c == "8" or c == "9" { name = name + c j = j + 1 continue }
// letters
if (c >= "A" and c <= "Z") or (c >= "a" and c <= "z") { name = name + c j = j + 1 continue }
break
}
// skip to '{'
loop (j < n) {
@c2 = text.substring(j, j+1)
if c2 == "{" { break }
j = j + 1
}
@end = _find_balanced_object_end(text, j)
if end < 0 { end = j }
@obj = new MapBox()
obj.set("name", name)
obj.set("start", _int_to_str(p))
obj.set("end", _int_to_str(end))
res.push(obj)
i = end + 1
}
return res
}
// Print duplicate boxes by name
_report_duplicate_boxes(text) {
@boxes = _scan_boxes(text)
@cnt = new MapBox()
@names = new ArrayBox()
@i = 0
loop (i < boxes.size()) {
@name = boxes.get(i).get("name")
@cur = cnt.get(name)
if cur == null { cnt.set(name, "1") names.push(name) } else { cnt.set(name, _int_to_str(_str_to_int(cur) + 1)) }
i = i + 1
}
@j = 0
loop (j < names.size()) {
@k = names.get(j)
@v = cnt.get(k)
if _str_to_int(v) > 1 { print("dup_box " + k + " x" + v) }
j = j + 1
}
return 0
}
// Inside a given box, count function name duplicates (simple scan: name(...){ )
_report_duplicate_functions_in_box(text, box_name) {
@boxes = _scan_boxes(text)
@i = 0
@fnmap = new MapBox()
@fnames = new ArrayBox()
loop (i < boxes.size()) {
@b = boxes.get(i)
if b.get("name") == box_name {
@s = _str_to_int(b.get("start"))
@e = _str_to_int(b.get("end"))
@j = s
loop (j < e) {
// find identifier start at line head-ish (naive)
// pattern: <spaces> ident '(' ... '{'
@k = j
// skip spaces/newlines
loop (k < e) {
@ch = text.substring(k, k+1)
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { k = k + 1 continue }
break
}
if k >= e { break }
// read ident
@name = ""
@p = k
@c0 = text.substring(p, p+1)
if (c0 >= "A" and c0 <= "Z") or (c0 >= "a" and c0 <= "z") or c0 == "_" {
loop (p < e) {
@c = text.substring(p, p+1)
if (c >= "A" and c <= "Z") or (c >= "a" and c <= "z") or (c >= "0" and c <= "9") or c == "_" { name = name + c p = p + 1 continue }
break
}
// must be a function definition: ident '(' ... ')' ws* '{'
if text.substring(p, p+1) == "(" {
// find matching ')' with a simple counter (strings ignored for simplicity)
@d = 0
@r = p
loop (r < e) {
@cc = text.substring(r, r+1)
if cc == "(" { d = d + 1 r = r + 1 continue }
if cc == ")" {
d = d - 1
r = r + 1
if d <= 0 { break }
continue
}
if cc == "\"" {
// skip string inside params
r = r + 1
loop (r < e) {
@c2 = text.substring(r, r+1)
if c2 == "\\" { r = r + 2 continue }
if c2 == "\"" { r = r + 1 break }
r = r + 1
}
continue
}
r = r + 1
}
// skip ws
loop (r < e) {
@ws = text.substring(r, r+1)
if ws == " " or ws == "\t" or ws == "\r" or ws == "\n" { r = r + 1 continue }
break
}
// definition only if next is '{'
if r < e and text.substring(r, r+1) == "{" {
@cur = fnmap.get(name)
if cur == null { fnmap.set(name, "1") fnames.push(name) } else { fnmap.set(name, _int_to_str(_str_to_int(cur) + 1)) }
}
}
}
// advance to next line
@nl = _index_of_from(text, "\n", k+1)
if nl < 0 || nl > e { break }
j = nl + 1
}
break
}
i = i + 1
}
@x = 0
loop (x < fnames.size()) {
@nm = fnames.get(x)
@ct = fnmap.get(nm)
if _str_to_int(ct) > 1 { print("dup_fn " + box_name + "." + nm + " x" + ct) }
x = x + 1
}
return 0
}
// Report summary
report(text) {
// find Main
@m = _index_of_from(text, "static box Main {", 0)
@delta = -9999
if m > 0 { delta = _brace_delta_ignoring_strings(text, 0, m) }
print("prelude_brace_delta=" + _int_to_str(delta))
// duplicate boxes
_report_duplicate_boxes(text)
// specific hot-spot
_report_duplicate_functions_in_box(text, "MiniVmPrints")
return 0
}
// Load dump file and report
analyze_dump_file(path) {
@fb = new FileBox()
@f = fb.open(path)
if f == null { print("warn: cannot open " + path) return 0 }
@text = f.read()
f.close()
return me.report(text)
}
}

View File

@ -0,0 +1,59 @@
// Self-contained dev smoke for FunctionCall empty-args
// Goal: echo() -> empty line, itoa() -> 0
static box MiniVm {
// simple substring find from position
index_of_from(hay, needle, pos) {
if pos < 0 { pos = 0 }
if pos >= hay.length() { return -1 }
local tail = hay.substring(pos, hay.length())
local rel = tail.indexOf(needle)
if rel < 0 { return -1 } else { return pos + rel }
}
// collect only FunctionCall with empty arguments [] for echo/itoa
collect_prints(json) {
local out = new ArrayBox()
local pos = 0
local guard = 0
loop (true) {
guard = guard + 1
if guard > 16 { break }
local k_fc = "\"kind\":\"FunctionCall\""
local p = index_of_from(json, k_fc, pos)
if p < 0 { break }
// name
local k_n = "\"name\":\""
local np = index_of_from(json, k_n, p)
if np < 0 { break }
local ni = np + k_n.length()
local nj = index_of_from(json, "\"", ni)
if nj < 0 { break }
local fname = json.substring(ni, nj)
// args [] detection
local k_a = "\"arguments\":["
local ap = index_of_from(json, k_a, nj)
if ap < 0 { break }
local rb = index_of_from(json, "]", ap)
if rb < 0 { break }
// no content between '[' and ']'
if rb == ap + k_a.length() {
if fname == "echo" { out.push("") }
if fname == "itoa" { out.push("0") }
}
pos = rb + 1
}
return out
}
}
static box Main {
main(args) {
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"FunctionCall\",\"name\":\"echo\",\"arguments\":[]}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"FunctionCall\",\"name\":\"itoa\",\"arguments\":[]}}]}"
local arr = new MiniVm().collect_prints(json)
local i = 0
loop (i < arr.size()) { print(arr.get(i)) i = i + 1 }
return 0
}
}

View File

@ -0,0 +1,15 @@
using selfhost.vm.core as MiniVm
static box Main {
main(args) {
local json = "{\"kind\":\"Program\",\"statements\":["
+ "{\"kind\":\"Print\",\"expression\":{\"kind\":\"FunctionCall\",\"name\":\"echo\",\"arguments\":[]}},"
+ "{\"kind\":\"Print\",\"expression\":{\"kind\":\"FunctionCall\",\"name\":\"itoa\",\"arguments\":[]}}]}"
local arr = new MiniVm().collect_prints(json)
local i = 0
loop (i < arr.size()) { print(arr.get(i)) i = i + 1 }
return 0
}
}

View File

@ -0,0 +1,30 @@
// Minimal self-contained eval (no using): collect the first Print(Literal int) and print it
static box Main {
// helper: find from position
index_of_from(hay, needle, pos) {
if pos < 0 { pos = 0 }
if pos >= hay.length() { return -1 }
local tail = hay.substring(pos, hay.length())
local rel = tail.indexOf(needle)
if rel < 0 { return -1 } else { return pos + rel }
}
main(args) {
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":42}}}]}"
// naive collect: first Print of Literal int
local ki = "\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local p = json.indexOf(ki)
if p >= 0 {
local i = p + ki.length()
local j = i
loop (true) {
local ch = json.substring(j, j+1)
if ch == "" { break }
if ch == "0" || ch == "1" || ch == "2" || ch == "3" || ch == "4" || ch == "5" || ch == "6" || ch == "7" || ch == "8" || ch == "9" { j = j + 1 continue }
break
}
local digits = json.substring(i, j)
if digits { print(digits) }
}
return 0
}
}

View File

@ -0,0 +1,282 @@
static box Main {
// --- minimal helpers (self-contained) ---
index_of_from(hay, needle, pos) {
if pos < 0 { pos = 0 }
if pos >= hay.length() { return -1 }
local tail = hay.substring(pos, hay.length())
local rel = tail.indexOf(needle)
if rel < 0 { return -1 }
return pos + rel
}
_is_digit(ch) {
if ch == "0" { return 1 }
if ch == "1" { return 1 }
if ch == "2" { return 1 }
if ch == "3" { return 1 }
if ch == "4" { return 1 }
if ch == "5" { return 1 }
if ch == "6" { return 1 }
if ch == "7" { return 1 }
if ch == "8" { return 1 }
if ch == "9" { return 1 }
return 0
}
read_digits(s, pos) {
if pos < 0 { return "" }
local n = s.length()
if pos >= n { return "" }
local i = pos
local out_start = -1
loop (i < n) {
local ch = s.substring(i, i+1)
if _is_digit(ch) == 1 { if out_start < 0 { out_start = i } i = i + 1 continue } else { break }
}
if out_start < 0 { return "" }
return s.substring(out_start, i)
}
find_balanced_object_end(json, idx) {
if idx < 0 { return -1 }
local n = json.length()
if idx >= n { return -1 }
local depth = 0
local in_str = 0
local i = idx
local iter = 0
loop (i < n) {
iter = iter + 1
if iter > 5000 { return -1 }
local ch = json.substring(i, i+1)
if in_str == 1 {
if ch == "\\" { i = i + 2 continue }
if ch == "\"" { in_str = 0 }
i = i + 1
continue
}
if ch == "\"" { in_str = 1 i = i + 1 continue }
if ch == "{" { depth = depth + 1 }
if ch == "}" { depth = depth - 1 if depth == 0 { return i + 1 } }
i = i + 1
}
return -1
}
// --- core: collect Print outputs in order ---
collect_prints(json) {
local out = new ArrayBox()
local pos = 0
local guard = 0
local k_print = "\"kind\":\"Print\""
loop (true) {
guard = guard + 1
if guard > 200 { break }
local p = index_of_from(json, k_print, pos)
if p < 0 { break }
// bound current Print slice as [current_print, next_print)
local obj_start = p
local next_p = index_of_from(json, k_print, p + k_print.length())
local obj_end = json.length()
if next_p > 0 { obj_end = next_p }
// 1) BinaryOp(int '+' int)
{
local k_expr = "\"expression\":{"
local epos = index_of_from(json, k_expr, obj_start)
if epos > 0 { if epos < obj_end {
local k_bo = "\"kind\":\"BinaryOp\""
local bpos = index_of_from(json, k_bo, epos)
if bpos > 0 { if bpos < obj_end {
local opok = index_of_from(json, "\"operator\":\"+\"", bpos)
if opok > 0 {
// typed left/right literal ints
local k_l = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local k_r = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local lp = index_of_from(json, k_l, bpos)
if lp > 0 { if lp < obj_end {
local ld = read_digits(json, lp + k_l.length())
if ld != "" {
local rp = index_of_from(json, k_r, lp + k_l.length())
if rp > 0 { if rp < obj_end {
local rd = read_digits(json, rp + k_r.length())
if rd != "" { out.push(_int_to_str(_str_to_int(ld) + _str_to_int(rd))) pos = p + k_print.length() continue }
}}
}
}}
// fallback: scan two successive '"value":' digits within expression bounds
local k_v = "\"value\":"
local v1 = index_of_from(json, k_v, epos)
if v1 > 0 { if v1 < obj_end {
local d1 = read_digits(json, v1 + k_v.length())
if d1 != "" {
local v2 = index_of_from(json, k_v, v1 + k_v.length())
if v2 > 0 { if v2 < obj_end {
local d2 = read_digits(json, v2 + k_v.length())
if d2 != "" { out.push(_int_to_str(_str_to_int(d1) + _str_to_int(d2))) pos = p + k_print.length() continue }
}}
}
}}
}
}}
}}
}
// 2) Compare(lhs/rhs ints): prints 1/0
{
local k_cp = "\"kind\":\"Compare\""
local cpos = index_of_from(json, k_cp, obj_start)
if cpos > 0 { if cpos < obj_end {
local k_op = "\"operation\":\""
local opos = index_of_from(json, k_op, cpos)
if opos > 0 { if opos < obj_end {
local oi = opos + k_op.length()
local oj = index_of_from(json, "\"", oi)
if oj > 0 { if oj <= obj_end {
local op = json.substring(oi, oj)
local k_v = "\"value\":"
local lhs_v = index_of_from(json, k_v, oj)
if lhs_v > 0 { if lhs_v < obj_end {
local la = read_digits(json, lhs_v + k_v.length())
if la != "" {
local rhs_v = index_of_from(json, k_v, lhs_v + k_v.length())
if rhs_v > 0 { if rhs_v < obj_end {
local rb = read_digits(json, rhs_v + k_v.length())
if rb != "" {
local ai = _str_to_int(la)
local bi = _str_to_int(rb)
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 } }
out.push(_int_to_str(res))
pos = p + k_print.length()
continue
}
}}
}
}}
}}
}}
}}
}
// 3) FunctionCall echo/itoa (single literal arg)
{
local k_fc = "\"kind\":\"FunctionCall\""
local fcp = index_of_from(json, k_fc, obj_start)
if fcp > 0 { if fcp < obj_end {
local kn = "\"name\":\""
local np = index_of_from(json, kn, fcp)
if np > 0 { if np < obj_end {
local ni = np + kn.length()
local nj = index_of_from(json, "\"", ni)
if nj > 0 { if nj <= obj_end {
local fname = json.substring(ni, nj)
local ka = "\"arguments\":["
local ap = index_of_from(json, ka, nj)
if ap > 0 { if ap < obj_end {
// string arg
local ks = "\"type\":\"string\",\"value\":\""
local ps = index_of_from(json, ks, ap)
if ps > 0 { if ps < obj_end {
local si = ps + ks.length()
local sj = index_of_from(json, "\"", si)
if sj > 0 { if sj <= obj_end {
local sval = json.substring(si, sj)
if fname == "echo" { out.push(sval) pos = p + k_print.length() continue }
}}
}}
// int arg
local ki = "\"type\":\"int\",\"value\":"
local pi = index_of_from(json, ki, ap)
if pi > 0 { if pi < obj_end {
local ival = read_digits(json, pi + ki.length())
if ival != "" { if fname == "itoa" { out.push(ival) pos = p + k_print.length() continue } else { if fname == "echo" { out.push(ival) pos = p + k_print.length() continue } } }
}}
}}
}}
}}
}}
}
// 4) Literal string
{
local ks = "\"type\":\"string\",\"value\":\""
local ps = index_of_from(json, ks, obj_start)
if ps > 0 { if ps < obj_end {
local si = ps + ks.length()
local sj = index_of_from(json, "\"", si)
if sj > 0 { if sj <= obj_end { out.push(json.substring(si, sj)) pos = p + k_print.length() continue }}
}}
}
// 5) Literal int
{
local ki = "\"type\":\"int\",\"value\":"
local pi = index_of_from(json, ki, obj_start)
if pi > 0 { if pi < obj_end {
local digits = read_digits(json, pi + ki.length())
if digits != "" { out.push(digits) pos = p + k_print.length() continue }
}}
}
// Unknown: skip ahead
pos = p + k_print.length()
if pos <= p { pos = p + 1 }
}
return out
}
// int<->str (non-negative only)
_str_to_int(s) {
local n = 0
local i = 0
loop (i < s.length()) {
local ch = s.substring(i, i+1)
n = n * 10
if ch == "0" { n = n + 0 }
if ch == "1" { n = n + 1 }
if ch == "2" { n = n + 2 }
if ch == "3" { n = n + 3 }
if ch == "4" { n = n + 4 }
if ch == "5" { n = n + 5 }
if ch == "6" { n = n + 6 }
if ch == "7" { n = n + 7 }
if ch == "8" { n = n + 8 }
if ch == "9" { n = n + 9 }
i = i + 1
}
return n
}
_int_to_str(n) {
if n == 0 { return "0" }
local s = new ArrayBox()
local x = n
loop (x > 0) {
local d = x % 10
if d == 0 { s.push("0") }
if d == 1 { s.push("1") }
if d == 2 { s.push("2") }
if d == 3 { s.push("3") }
if d == 4 { s.push("4") }
if d == 5 { s.push("5") }
if d == 6 { s.push("6") }
if d == 7 { s.push("7") }
if d == 8 { s.push("8") }
if d == 9 { s.push("9") }
x = (x - d) / 10
}
local out = new ArrayBox()
local i = s.size() - 1
loop (i >= 0) { out.push(s.get(i)) i = i - 1 }
local j = 0
local acc = ""
loop (j < out.size()) { acc = acc + out.get(j) j = j + 1 }
return acc
}
main(args) {
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"string\",\"value\":\"A\"}}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"FunctionCall\",\"name\":\"echo\",\"arguments\":[{\"kind\":\"Literal\",\"value\":{\"type\":\"string\",\"value\":\"B\"}}]}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"FunctionCall\",\"name\":\"itoa\",\"arguments\":[{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":7}}]}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"Compare\",\"operation\":\"<\",\"lhs\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":1}},\"rhs\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":2}}}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"BinaryOp\",\"operator\":\"+\",\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":3}},\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":4}}}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":5}}}]}"
local arr = collect_prints(json)
local i = 0
loop (i < arr.size()) { print(arr.get(i)) i = i + 1 }
return 0
}
}

View File

@ -0,0 +1,18 @@
using selfhost.vm.core as MiniVm
static box Main {
main(args) {
// Mixed shapes (loader-centric)
// 1) echo() with empty args -> ""
// 2) string literal with quotes inside
// 3) itoa() with empty args -> "0"
// 4) BinaryOp 8+9
// 5) int literal 4
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"FunctionCall\",\"name\":\"echo\",\"arguments\":[]}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"string\",\"value\":\"C\"}}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"FunctionCall\",\"name\":\"itoa\",\"arguments\":[]}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"BinaryOp\",\"operator\":\"+\",\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":8}},\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":9}}}},{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":4}}}]}"
local arr = new MiniVm().collect_prints(json)
local i = 0
loop (i < arr.size()) { print(arr.get(i)) i = i + 1 }
return 0
}
}

View File

@ -1,309 +1,12 @@
// Mini-VM: function-based entry using library
using selfhost.vm.json_cur as MiniJsonCur
using selfhost.vm.json as MiniJson
// Thin entry: delegate to core MiniVm
// Using is pre-inlined by runner; keep entry minimal for maintainability.
using selfhost.vm.core as MiniVm
// Local static box (duplicated from mini_vm_lib for now to avoid include gate issues)
// Program entry: prefer argv[0] JSON, fallback to embedded sample
static box Main {
// Small helpers for quick JSON scans (avoid cross-box deps)
index_of_from(hay, needle, pos) {
local tail = hay.substring(pos, hay.length())
local rel = tail.indexOf(needle)
if rel < 0 { return -1 } else { return pos + rel }
}
_is_digit(ch) {
if ch == "0" { return 1 }
if ch == "1" { return 1 }
if ch == "2" { return 1 }
if ch == "3" { return 1 }
if ch == "4" { return 1 }
if ch == "5" { return 1 }
if ch == "6" { return 1 }
if ch == "7" { return 1 }
if ch == "8" { return 1 }
if ch == "9" { return 1 }
return 0
}
_digit_value(ch) {
if ch == "0" { return 0 }
if ch == "1" { return 1 }
if ch == "2" { return 2 }
if ch == "3" { return 3 }
if ch == "4" { return 4 }
if ch == "5" { return 5 }
if ch == "6" { return 6 }
if ch == "7" { return 7 }
if ch == "8" { return 8 }
if ch == "9" { return 9 }
return 0
}
_str_to_int(s) {
local i = 0
local n = s.length()
local acc = 0
loop (i < n) {
local ch = s.substring(i, i+1)
if ch == "0" { acc = acc * 10 + 0 i = i + 1 continue }
if ch == "1" { acc = acc * 10 + 1 i = i + 1 continue }
if ch == "2" { acc = acc * 10 + 2 i = i + 1 continue }
if ch == "3" { acc = acc * 10 + 3 i = i + 1 continue }
if ch == "4" { acc = acc * 10 + 4 i = i + 1 continue }
if ch == "5" { acc = acc * 10 + 5 i = i + 1 continue }
if ch == "6" { acc = acc * 10 + 6 i = i + 1 continue }
if ch == "7" { acc = acc * 10 + 7 i = i + 1 continue }
if ch == "8" { acc = acc * 10 + 8 i = i + 1 continue }
if ch == "9" { acc = acc * 10 + 9 i = i + 1 continue }
break
}
return acc
}
main(args) {
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":42}}}]}"
// If args provided, use the first as JSON source (guard None)
if args {
if args.size() > 0 {
local s = args.get(0)
if s { json = s }
}
}
// Top-level fast paths for simple Print cases (Literal/FunctionCall/Compare/BinaryOp)
// 0) If(literal int) then/else branch (string prints)
if json.indexOf("\"If\"") >= 0 {
local kc = "\"condition\""
local pc = json.indexOf(kc)
if pc >= 0 {
local kv = "\"type\":\"int\",\"value\":"
local pv = json.indexOf(kv, pc)
if pv >= 0 {
local vi = pv + kv.length()
local ve = json.indexOf("}", vi)
if ve >= 0 {
local cond_str = json.substring(vi, ve)
// parse int
local a = 0
local i = 0
loop (i < cond_str.length()) {
local ch = cond_str.substring(i, i+1)
if ch == "0" { a = a*10 i = i + 1 continue }
if ch == "1" { a = a*10+1 i = i + 1 continue }
if ch == "2" { a = a*10+2 i = i + 1 continue }
if ch == "3" { a = a*10+3 i = i + 1 continue }
if ch == "4" { a = a*10+4 i = i + 1 continue }
if ch == "5" { a = a*10+5 i = i + 1 continue }
if ch == "6" { a = a*10+6 i = i + 1 continue }
if ch == "7" { a = a*10+7 i = i + 1 continue }
if ch == "8" { a = a*10+8 i = i + 1 continue }
if ch == "9" { a = a*10+9 i = i + 1 continue }
break
}
local truthy = 0
if a != 0 { truthy = 1 }
local bkey = "\"then_body\""
if truthy == 0 { bkey = "\"else_body\"" }
local pb = json.indexOf(bkey, ve)
if pb >= 0 {
// search first string literal value inside the chosen body
local ts = "\"type\":\"string\",\"value\":\""
local ps = json.indexOf(ts, pb)
if ps >= 0 {
local si = ps + ts.length()
local sj = json.indexOf("\"", si)
if sj >= 0 { print(json.substring(si, sj)) return 0 }
}
}
}
}
}
}
// 1) Print(Literal string)
if json.indexOf("\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"string\"") >= 0 {
local ks = "\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"string\",\"value\":\""
local ps = json.indexOf(ks)
if ps >= 0 {
local si = ps + ks.length()
local sj = json.indexOf("\"", si)
if sj >= 0 { print(json.substring(si, sj)) return 0 }
}
}
// 2) Print(Literal int)
if json.indexOf("\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\"") >= 0 {
local ki = "\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local pi = json.indexOf(ki)
if pi >= 0 {
local ii = pi + ki.length()
// take until next non-digit or closing brace
local ie = json.indexOf("}", ii)
if ie < 0 { ie = ii }
local d = json.substring(ii, ie)
print(d)
return 0
}
}
// 3) Print(FunctionCall) minimal (echo/itoa)
if json.indexOf("\"FunctionCall\"") >= 0 {
local pos = 0
loop (true) {
local kfc = "\"kind\":\"FunctionCall\""
local fcp = json.indexOf(kfc, pos)
if fcp < 0 { break }
local kn = "\"name\":\""
local pn = json.indexOf(kn, fcp)
if pn < 0 { break }
local ni = pn + kn.length()
local nj = json.indexOf("\"", ni)
if nj < 0 { break }
local fname = json.substring(ni, nj)
local ka = "\"arguments\":["
local pa = json.indexOf(ka, nj)
if pa < 0 { pos = nj + 1 continue }
// string arg
local ts = "\"type\":\"string\",\"value\":\""
local ti = json.indexOf(ts, pa)
if ti >= 0 {
local si = ti + ts.length()
local sj = json.indexOf("\"", si)
if sj >= 0 {
local sval = json.substring(si, sj)
if fname == "echo" { print(sval) }
pos = sj + 1
continue
}
}
// int arg
local ti2 = json.indexOf("\"type\":\"int\",\"value\":", pa)
if ti2 >= 0 {
local vi = ti2 + "\"type\":\"int\",\"value\":".length()
local ve = json.indexOf("}", vi)
if ve < 0 { ve = vi }
local ival = json.substring(vi, ve)
if fname == "itoa" || fname == "echo" { print(ival) }
pos = ve + 1
continue
}
pos = pn + 1
}
return 0
}
// 4) Print(Compare) minimal
if json.indexOf("\"Compare\"") >= 0 {
local ko = "\"operation\":\""
local po = json.indexOf(ko)
if po >= 0 {
local oi = po + ko.length()
local oj = json.indexOf("\"", oi)
if oj >= 0 {
local op = json.substring(oi, oj)
local kv = "\"value\":\""
// lhs int
local kl = "\"lhs\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local pl = json.indexOf(kl, oj)
if pl >= 0 {
local li = pl + kl.length()
local le = json.indexOf("}", li)
if le >= 0 {
local la = json.substring(li, le)
// rhs int
local kr = "\"rhs\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
local pr = json.indexOf(kr, le)
if pr >= 0 {
local ri = pr + kr.length()
local re = json.indexOf("}", ri)
if re >= 0 {
local rb = json.substring(ri, re)
// compute
local a = 0
local i = 0
loop (i < la.length()) { local ch = la.substring(i, i+1) if ch == "0" { a = a*10+0 i=i+1 continue } if ch == "1" { a=a*10+1 i=i+1 continue } if ch == "2" { a=a*10+2 i=i+1 continue } if ch == "3" { a=a*10+3 i=i+1 continue } if ch == "4" { a=a*10+4 i=i+1 continue } if ch == "5" { a=a*10+5 i=i+1 continue } if ch == "6" { a=a*10+6 i=i+1 continue } if ch == "7" { a=a*10+7 i=i+1 continue } if ch == "8" { a=a*10+8 i=i+1 continue } if ch == "9" { a=a*10+9 i=i+1 continue } break }
local b = 0
local j = 0
loop (j < rb.length()) { local ch2 = rb.substring(j, j+1) if ch2 == "0" { b = b*10+0 j=j+1 continue } if ch2 == "1" { b=b*10+1 j=j+1 continue } if ch2 == "2" { b=b*10+2 j=j+1 continue } if ch2 == "3" { b=b*10+3 j=j+1 continue } if ch2 == "4" { b=b*10+4 j=j+1 continue } if ch2 == "5" { b=b*10+5 j=j+1 continue } if ch2 == "6" { b=b*10+6 j=j+1 continue } if ch2 == "7" { b=b*10+7 j=j+1 continue } if ch2 == "8" { b=b*10+8 j=j+1 continue } if ch2 == "9" { b=b*10+9 j=j+1 continue } break }
local res = 0
if op == "<" { if a < b { res = 1 } }
if op == "==" { if a == b { res = 1 } }
if op == "<=" { if a <= b { res = 1 } }
if op == ">" { if a > b { res = 1 } }
if op == ">=" { if a >= b { res = 1 } }
if op == "!=" { if a != b { res = 1 } }
print(res)
return 0
}
}
}
}
}
}
}
// 5) BinaryOp(int+int) typed pattern twice and add保険
if json.indexOf("\"BinaryOp\"") >= 0 && json.indexOf("\"operator\":\"+\"") >= 0 {
local pat = "\"type\":\"int\",\"value\":"
local p1 = json.indexOf(pat)
if p1 >= 0 {
// parse first integer by taking until next closing brace
local i = p1 + pat.length()
local end1 = json.indexOf("}", i)
if end1 < 0 { end1 = i }
local a_str = json.substring(i, end1)
// convert a_str to int
local a = 0
local k = 0
loop (k < a_str.length()) {
local ch = a_str.substring(k, k+1)
if ch == "0" { a = a * 10 + 0 k = k + 1 continue }
if ch == "1" { a = a * 10 + 1 k = k + 1 continue }
if ch == "2" { a = a * 10 + 2 k = k + 1 continue }
if ch == "3" { a = a * 10 + 3 k = k + 1 continue }
if ch == "4" { a = a * 10 + 4 k = k + 1 continue }
if ch == "5" { a = a * 10 + 5 k = k + 1 continue }
if ch == "6" { a = a * 10 + 6 k = k + 1 continue }
if ch == "7" { a = a * 10 + 7 k = k + 1 continue }
if ch == "8" { a = a * 10 + 8 k = k + 1 continue }
if ch == "9" { a = a * 10 + 9 k = k + 1 continue }
break
}
// parse second integer
local p2 = json.lastIndexOf(pat)
if p2 >= 0 && p2 != p1 {
local j = p2 + pat.length()
local end2 = json.indexOf("}", j)
if end2 < 0 { end2 = j }
local b_str = json.substring(j, end2)
local b = 0
local m = 0
loop (m < b_str.length()) {
local ch2 = b_str.substring(m, m+1)
if ch2 == "0" { b = b * 10 + 0 m = m + 1 continue }
if ch2 == "1" { b = b * 10 + 1 m = m + 1 continue }
if ch2 == "2" { b = b * 10 + 2 m = m + 1 continue }
if ch2 == "3" { b = b * 10 + 3 m = m + 1 continue }
if ch2 == "4" { b = b * 10 + 4 m = m + 1 continue }
if ch2 == "5" { b = b * 10 + 5 m = m + 1 continue }
if ch2 == "6" { b = b * 10 + 6 m = m + 1 continue }
if ch2 == "7" { b = b * 10 + 7 m = m + 1 continue }
if ch2 == "8" { b = b * 10 + 8 m = m + 1 continue }
if ch2 == "9" { b = b * 10 + 9 m = m + 1 continue }
break
}
print(a + b)
return 0
}
}
}
// Fallback to full MiniVm runner
local vm = new MiniVm()
return vm.run(json)
@json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":42}}}]}"
if args { if args.size() > 0 { @s = args.get(0) if s { json = s } } }
return new MiniVm().run(json)
}
}
// Top-level fallback entry for current runner
function main(args) {
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":42}}}]}"
if args {
if args.size() > 0 {
local s = args.get(0)
if s { json = s }
}
}
local vm = new MiniVm()
return vm.run(json)
}

View File

@ -3,23 +3,23 @@
static box MiniVm {
_is_digit(ch) { return ch == "0" || ch == "1" || ch == "2" || ch == "3" || ch == "4" || ch == "5" || ch == "6" || ch == "7" || ch == "8" || ch == "9" }
read_digits(json, pos) {
local out = ""
@out = ""
loop (true) {
local s = json.substring(pos, pos+1)
@s = json.substring(pos, pos+1)
if s == "" { break }
if _is_digit(s) { out = out + s pos = pos + 1 } else { break }
}
return out
}
parse_first_int(json) {
local key = "\"value\":{\"type\":\"int\",\"value\":"
local idx = json.lastIndexOf(key)
@key = "\"value\":{\"type\":\"int\",\"value\":"
@idx = json.lastIndexOf(key)
if idx < 0 { return "0" }
local start = idx + key.length()
@start = idx + key.length()
return read_digits(json, start)
}
run_branch(json) {
local n = parse_first_int(json)
@n = parse_first_int(json)
if n == "0" || n == "1" || n == "2" || n == "3" || n == "4" { print("10") return 0 }
print("20")
return 0
@ -29,12 +29,12 @@ static box MiniVm {
// Program entry: embedded JSON (value=1 → print 10; else → 20)
static box Main {
main(args) {
local vm = new MiniVm()
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":1}}}]}"
@vm = new MiniVm()
@json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":1}}}]}"
// If provided, override by argv[0]
if args {
if args.size() > 0 {
local s = args.get(0)
@s = args.get(0)
if s { json = s }
}
}
@ -44,15 +44,15 @@ static box Main {
// Top-level fallback entry for current runner
function main(args) {
local vm = new MiniVm()
local json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":1}}}]}"
@vm = new MiniVm()
@json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":1}}}]}"
if args {
if args.size() > 0 {
local s = args.get(0)
@s = args.get(0)
if s { json = s }
}
}
local n = vm.parse_first_int(json)
@n = vm.parse_first_int(json)
if n == "0" || n == "1" || n == "2" || n == "3" || n == "4" { print("10") return 0 }
print("20")
return 0

View File

@ -6,9 +6,9 @@ static box MiniVm {
// Read consecutive digits starting at pos
read_digits(json, pos) {
local out = ""
@out = ""
loop (true) {
local s = json.substring(pos, pos+1)
@s = json.substring(pos, pos+1)
if s == "" { break }
if _is_digit(s) { out = out + s pos = pos + 1 } else { break }
}
@ -17,16 +17,16 @@ static box MiniVm {
// Extract the first integer literal from our AST JSON v0 subset
parse_first_int(json) {
local key = "\"value\":{\"type\":\"int\",\"value\":"
local idx = json.lastIndexOf(key)
@key = "\"value\":{\"type\":\"int\",\"value\":"
@idx = json.lastIndexOf(key)
if idx < 0 { return "0" }
local start = idx + key.length()
@start = idx + key.length()
return read_digits(json, start)
}
// Execute a minimal program: print the extracted integer and exit code 0
run(json) {
local n = parse_first_int(json)
@n = parse_first_int(json)
print(n)
return 0
}

View File

@ -0,0 +1,16 @@
using selfhost.vm.core as MiniVm
static box Main {
main(args) {
@json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":42}}}]}"
if args { if args.size() > 0 { @s = args.get(0) if s { json = s } } }
print("pre")
print(json.length())
print(json.indexOf("\"kind\":\"Program\""))
print(json.indexOf("\"kind\":\"Print\""))
print(json.indexOf("\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\""))
@code = new MiniVm().run(json)
print("post")
return code
}
}