Remove legacy apps/selfhost tree; intentionally break remaining references to surface migrations (moving to lang/)
This commit is contained in:
@ -1,20 +0,0 @@
|
||||
# Self‑Hosting Apps (Ny‑only)
|
||||
|
||||
Purpose
|
||||
- Keep self‑hosting Ny scripts isolated from general `apps/` noise.
|
||||
- Enable fast inner loop without touching Rust unless necessary.
|
||||
|
||||
Conventions
|
||||
- Entry scripts live under this folder; prefer minimal dependencies.
|
||||
- Use `--using-path apps/selfhost:apps` when resolving modules.
|
||||
- Prefer VM (`--backend vm`) for speed and stability.
|
||||
|
||||
Quickstart
|
||||
- Run minimal sample: `make dev` (uses `apps/selfhost-minimal/main.nyash`)
|
||||
- Watch changes: `make dev-watch`
|
||||
- Run parser Ny project: `./tools/dev_selfhost_loop.sh --std -v --backend vm -- apps/selfhost/ny-parser-nyash/main.nyash`
|
||||
|
||||
Guidelines
|
||||
- Keep files small and composable; avoid cross‑project coupling.
|
||||
- If moving an existing `apps/*` item here, update docs/scripts accordingly.
|
||||
- For namespace usage, pass `--using-path apps/selfhost:apps`.
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,277 +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 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.length())
|
||||
if ld != "" {
|
||||
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
|
||||
if rp > 0 { if rp < slice_end {
|
||||
local rd = scan.read_digits(json, rp + k_rint.length())
|
||||
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.length())
|
||||
if ld != "" {
|
||||
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
|
||||
if rp > 0 { if rp < obj_end2 {
|
||||
local rd = scan.read_digits(json, rp + k_rint.length())
|
||||
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.length())
|
||||
if ld != "" {
|
||||
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
|
||||
if rp > 0 { if rp < obj_end {
|
||||
local rd = scan.read_digits(json, rp + k_rint.length())
|
||||
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.length())
|
||||
if ld != "" {
|
||||
local rp = scan.index_of_from(json, k_rint, lp + k_lint.length())
|
||||
if rp > 0 { if rp < obj_end {
|
||||
local rd = scan.read_digits(json, rp + k_rint.length())
|
||||
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 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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,205 +0,0 @@
|
||||
// Mini-VM scanning and numeric helpers
|
||||
static box MiniVmScan {
|
||||
// helper: find needle from position pos
|
||||
index_of_from(hay, needle, pos) {
|
||||
// Guard position; clamp to valid range to avoid negative substring
|
||||
if pos < 0 { pos = 0 }
|
||||
local n = hay.length()
|
||||
if pos >= n { return -1 }
|
||||
local m = needle.length()
|
||||
if m <= 0 { return pos }
|
||||
local i = pos
|
||||
local limit = n - m
|
||||
loop (i <= limit) {
|
||||
local seg = hay.substring(i, i + m)
|
||||
if seg == needle { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// helper: find balanced bracket range [ ... ] starting at idx (points to '[')
|
||||
find_balanced_array_end(json, idx) {
|
||||
@n = json.length()
|
||||
if json.substring(idx, idx+1) != "[" { return -1 }
|
||||
@depth = 0
|
||||
@i = idx
|
||||
@in_str = 0
|
||||
@guard = 0
|
||||
loop (i < n) {
|
||||
guard = guard + 1
|
||||
if guard > 50000 { return -1 }
|
||||
@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 }
|
||||
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 }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// helper: find balanced object range { ... } starting at idx (points to '{')
|
||||
find_balanced_object_end(json, idx) {
|
||||
@n = json.length()
|
||||
if json.substring(idx, idx+1) != "{" { return -1 }
|
||||
@depth = 0
|
||||
@i = idx
|
||||
@in_str = 0
|
||||
@guard = 0
|
||||
loop (i < n) {
|
||||
guard = guard + 1
|
||||
if guard > 50000 { return -1 }
|
||||
@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 }
|
||||
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 }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
_str_to_int(s) {
|
||||
@i = 0
|
||||
@n = s.length()
|
||||
@acc = 0
|
||||
loop (i < n) {
|
||||
@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" }
|
||||
@v = n
|
||||
@out = ""
|
||||
loop (v > 0) {
|
||||
@d = v % 10
|
||||
@ch = _digit_char(d)
|
||||
out = ch + out
|
||||
v = v / 10
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Read digit runs starting at pos
|
||||
read_digits(json, pos) {
|
||||
@out = ""
|
||||
loop (true) {
|
||||
@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) {
|
||||
@i = 0
|
||||
@n = json.length()
|
||||
@total = 0
|
||||
loop (i < n) {
|
||||
@ch = json.substring(i, i+1)
|
||||
if ch == "\"" {
|
||||
@j = index_of_from(json, "\"", i+1)
|
||||
if j < 0 { break }
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
@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) {
|
||||
@i = 0
|
||||
@n = json.length()
|
||||
@total = 0
|
||||
loop (i < n) {
|
||||
@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) {
|
||||
@i = 0
|
||||
@n = json.length()
|
||||
@total = 0
|
||||
local found = 0
|
||||
loop (i < n) {
|
||||
@ch = json.substring(i, i+1)
|
||||
if ch == "\"" {
|
||||
@j = index_of_from(json, "\"", i+1)
|
||||
if j < 0 { break }
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
@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 ""
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
# Nyash Selfhost Compiler (MVP scaffold)
|
||||
|
||||
This is the Phase 15.3 work-in-progress Nyash compiler implemented in Ny.
|
||||
|
||||
Layout
|
||||
- `compiler.nyash`: entry (CompilerBox). Reads `tmp/ny_parser_input.ny`, prints JSON v0.
|
||||
- `parser/`: lexer/parser/ast (scaffolds; to be filled as we extend Stage‑2)
|
||||
- `mir/`: builder/optimizer stubs (future; current target is JSON v0 emit)
|
||||
- `tests/`: Stage‑1/2 samples (TBD)
|
||||
|
||||
Run (behind flag)
|
||||
- `NYASH_USE_NY_COMPILER=1 target/release/nyash --backend vm <program.nyash>`
|
||||
- The runner writes the input to `tmp/ny_parser_input.ny` and invokes this program.
|
||||
- It captures a JSON v0 line from stdout and executes it via the JSON bridge.
|
||||
- Stage‑3 syntax gate: set `NYASH_NY_COMPILER_STAGE3=1` to pass `--stage3` to the parser (accepts Break/Continue/Throw/Try in JSON v0).
|
||||
|
||||
Notes
|
||||
- Early MVP emits a minimal JSON v0 (currently a placeholder: return 0). We will gradually wire lexer/parser/emitter.
|
||||
- Keep JSON v0 spec in `docs/reference/ir/json_v0.md`.
|
||||
@ -1,38 +0,0 @@
|
||||
// DebugBox — conditional debug output aggregator (extracted)
|
||||
box DebugBox {
|
||||
enabled
|
||||
birth() {
|
||||
me.enabled = 0
|
||||
return 0
|
||||
}
|
||||
set_enabled(v) {
|
||||
me.enabled = v
|
||||
return 0
|
||||
}
|
||||
log(msg) {
|
||||
if me.enabled {
|
||||
local c = new ConsoleBox()
|
||||
c.println("[DEBUG] " + msg)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
info(msg) {
|
||||
if me.enabled {
|
||||
local c = new ConsoleBox()
|
||||
c.println("[INFO] " + msg)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
error(msg) {
|
||||
if me.enabled {
|
||||
local c = new ConsoleBox()
|
||||
c.println("[ERROR] " + msg)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Include stub to satisfy current include lowering (expects a static box)
|
||||
static box DebugStub {
|
||||
main(args) { return 0 }
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
// EmitterBox — thin wrapper to emit JSON v0 (extracted)
|
||||
box EmitterBox {
|
||||
emit_program(json, usings_json) {
|
||||
if json == null { return json }
|
||||
// Normalize usings payload to at least an empty array so meta.usings is always present
|
||||
local payload = usings_json
|
||||
if payload == null { payload = "[]" }
|
||||
if payload.length() == 0 { payload = "[]" }
|
||||
// Inject meta.usings before closing brace of top-level object
|
||||
local n = json.lastIndexOf("}")
|
||||
if n < 0 { return json }
|
||||
local head = json.substring(0, n)
|
||||
local tail = json.substring(n, json.length())
|
||||
local needs_comma = 1
|
||||
if head.length() == 0 { needs_comma = 0 } else {
|
||||
local last = head.substring(head.length() - 1, head.length())
|
||||
if last == "{" || last == "," { needs_comma = 0 }
|
||||
}
|
||||
if needs_comma == 1 { head = head + "," }
|
||||
return head + "\"meta\":{\"usings\":" + payload + "}" + tail
|
||||
}
|
||||
}
|
||||
|
||||
static box EmitterStub {
|
||||
main(args) { return 0 }
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
// MirEmitterBox — Minimal MIR JSON v0 emitter (M2 MVP)
|
||||
// Scope: Return(Int) only (const + ret). Safe default to 0 when not found.
|
||||
// Future: add Binary/Compare/ExternCall/BoxCall lowering incrementally.
|
||||
|
||||
box MirEmitterBox {
|
||||
// Extract first Return(Int) value from Stage-1 JSON (very small string scan)
|
||||
_extract_return_int(ast_json) {
|
||||
if ast_json == null { return 0 }
|
||||
// Look for '"type":"Return"'
|
||||
local p = ast_json.lastIndexOf("\"type\":\"Return\"")
|
||||
if p < 0 { p = ast_json.indexOf("\"type\":\"Return\"") }
|
||||
if p < 0 { return 0 }
|
||||
// From there, search for '"type":"Int","value":'
|
||||
local q = ast_json.indexOf("\"type\":\"Int\",\"value\":", p)
|
||||
if q < 0 { return 0 }
|
||||
q = q + 23 // length of the marker
|
||||
// Read consecutive digits (optional minus not handled in Stage-1 yet)
|
||||
local n = ast_json.length()
|
||||
local i = q
|
||||
local s = ""
|
||||
loop(i < n) {
|
||||
local ch = ast_json.substring(i, i+1)
|
||||
if ch >= "0" && ch <= "9" { s = s + ch i = i + 1 } else { break }
|
||||
}
|
||||
if s.length() == 0 { return 0 }
|
||||
// String to int via addition loop
|
||||
local val = 0
|
||||
local k = 0
|
||||
local m = s.length()
|
||||
loop(k < m) {
|
||||
local d = s.substring(k, k+1)
|
||||
local dv = 0
|
||||
if d == "1" { dv = 1 } else { if d == "2" { dv = 2 } else { if d == "3" { dv = 3 } else { if d == "4" { dv = 4 } else { if d == "5" { dv = 5 } else { if d == "6" { dv = 6 } else { if d == "7" { dv = 7 } else { if d == "8" { dv = 8 } else { if d == "9" { dv = 9 } } } } } } } } }
|
||||
val = val * 10 + dv
|
||||
k = k + 1
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// Build minimal MIR JSON v0: main with const -> ret
|
||||
emit_mir_min(ast_json) {
|
||||
local retv = me._extract_return_int(ast_json)
|
||||
local s = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"blocks\":[{\"id\":0,\"instructions\":["
|
||||
s = s + "{\\\"op\\\":\\\"const\\\",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + retv + "},\\\"dst\\\":1},"
|
||||
s = s + "{\\\"op\\\":\\\"ret\\\",\\\"value\\\":1}"
|
||||
s = s + "]}]}]}"
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
static box MirEmitterStub { main(args) { return 0 } }
|
||||
|
||||
@ -1,917 +0,0 @@
|
||||
// ParserBox — Stage‑1 JSON v0 generator(extracted, simplified for Rust parser)
|
||||
box ParserBox {
|
||||
gpos
|
||||
usings_json
|
||||
stage3
|
||||
|
||||
birth() { me.gpos = 0 me.usings_json = "[]" me.stage3 = 0 return 0 }
|
||||
|
||||
stage3_enable(flag) {
|
||||
if flag == null { flag = 0 }
|
||||
if flag == 0 { me.stage3 = 0 } else { me.stage3 = 1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
stage3_enabled() {
|
||||
if me.stage3 == 1 { return 1 }
|
||||
return 0
|
||||
}
|
||||
|
||||
esc_json(s) {
|
||||
local out = ""
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "\\" { out = out + "\\\\" } else { if ch == "\"" { out = out + "\\\"" } else { out = out + ch } }
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
is_digit(ch) { return ch >= "0" && ch <= "9" }
|
||||
is_space(ch) { return ch == " " || ch == "\t" || ch == "\n" || ch == "\r" }
|
||||
// simple alpha/underscore check for identifiers
|
||||
is_alpha(ch) { return (ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z") || ch == "_" }
|
||||
|
||||
gpos_set(i) { me.gpos = i return 0 }
|
||||
gpos_get() { return me.gpos }
|
||||
|
||||
// lightweight string helpers
|
||||
starts_with(src, i, pat) {
|
||||
local n = src.length()
|
||||
local m = pat.length()
|
||||
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
|
||||
}
|
||||
index_of(src, i, pat) {
|
||||
local n = src.length()
|
||||
local m = pat.length()
|
||||
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(s) {
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
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)
|
||||
}
|
||||
|
||||
// Enhanced whitespace skipper (inline lines): used by line-based using extractor
|
||||
trim_ws_and_line_comments(s) {
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
// leading spaces/tabs
|
||||
loop(i < n && (s.substring(i,i+1) == " " || s.substring(i,i+1) == "\t")) { i = i + 1 }
|
||||
// strip line comments
|
||||
if i + 1 < n && s.substring(i, i+2) == "//" { return "" }
|
||||
return s.substring(i, n)
|
||||
}
|
||||
|
||||
// keyword match at position i 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.length()
|
||||
local j = i + kw.length()
|
||||
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
|
||||
}
|
||||
|
||||
// integer to string (uses string concat coercion)
|
||||
i2s(v) { return "" + v }
|
||||
|
||||
// Read identifier starting at i: [A-Za-z_][A-Za-z0-9_]*; returns "name@pos"
|
||||
read_ident2(src, i) {
|
||||
local n = src.length()
|
||||
local j = i
|
||||
if j >= n { return "@" + me.i2s(i) }
|
||||
local ch = src.substring(j, j+1)
|
||||
if me.is_alpha(ch) == 0 { return "@" + me.i2s(i) }
|
||||
j = j + 1
|
||||
loop(j < n) {
|
||||
local c = src.substring(j, j+1)
|
||||
if me.is_alpha(c) || me.is_digit(c) { j = j + 1 } else { break }
|
||||
}
|
||||
local name = src.substring(i, j)
|
||||
return name + "@" + me.i2s(j)
|
||||
}
|
||||
|
||||
// Read string literal at i (i points to '"'); returns raw content (no quotes), updates gpos
|
||||
read_string_lit(src, i) {
|
||||
local n = src.length()
|
||||
local j = i
|
||||
if j >= n || src.substring(j, j+1) != "\"" { me.gpos_set(i) return "" }
|
||||
j = j + 1
|
||||
local out = ""
|
||||
local guard = 0
|
||||
local max = 200000
|
||||
loop(j < n) {
|
||||
if guard > max { break } else { guard = guard + 1 }
|
||||
local ch = src.substring(j, j+1)
|
||||
if ch == "\"" { j = j + 1 me.gpos_set(j) return out }
|
||||
if ch == "\\" && j + 1 < n {
|
||||
local nx = src.substring(j+1, j+2)
|
||||
if nx == "\"" { out = out + "\"" j = j + 2 } else { if nx == "\\" { out = out + "\\" j = j + 2 } else { if nx == "n" { out = out + "\n" j = j + 2 } else { if nx == "r" { out = out + "\r" j = j + 2 } else { if nx == "t" { out = out + "\t" j = j + 2 } else { if nx == "u" && j + 5 < n { out = out + src.substring(j, j+6) j = j + 6 } else { out = out + nx j = j + 2 } } } } } }
|
||||
} else { out = out + ch j = j + 1 }
|
||||
}
|
||||
me.gpos_set(j)
|
||||
return out
|
||||
}
|
||||
|
||||
// Append a using entry into usings_json (no-op acceptance path)
|
||||
add_using(kind, target, alias) {
|
||||
// kind: "path" or "ns"; target: path or namespace; alias: nullable
|
||||
local cur = me.usings_json
|
||||
if cur == null || cur.length() == 0 { cur = "[]" }
|
||||
// Build entry
|
||||
local name = ""
|
||||
local path = null
|
||||
if kind == "path" {
|
||||
path = target
|
||||
if alias != null { name = alias } else {
|
||||
local p = target
|
||||
// basename
|
||||
local idx = -1
|
||||
local t = 0
|
||||
loop(t < p.length()) { if p.substring(t,t+1) == "/" { idx = t } t = t + 1 }
|
||||
if idx >= 0 { p = p.substring(idx+1, p.length()) }
|
||||
if p.length() > 6 && me.starts_with(p, p.length()-6, ".nyash") == 1 { p = p.substring(0, p.length()-6) }
|
||||
name = p
|
||||
}
|
||||
} else {
|
||||
name = target
|
||||
if alias != null { name = alias }
|
||||
}
|
||||
local entry = "{\"name\":\"" + me.esc_json(name) + "\""
|
||||
if path != null { entry = entry + ",\"path\":\"" + me.esc_json(path) + "\"" }
|
||||
entry = entry + "}"
|
||||
// Insert before closing ']' of array
|
||||
if cur == "[]" { me.usings_json = "[" + entry + "]" return 0 }
|
||||
// naive append
|
||||
local pos = cur.lastIndexOf("]")
|
||||
if pos < 0 { me.usings_json = "[" + entry + "]" return 0 }
|
||||
me.usings_json = cur.substring(0, pos) + "," + entry + "]"
|
||||
return 0
|
||||
}
|
||||
|
||||
// Collect `using` lines into JSON array stored in me.usings_json (no-op acceptance)
|
||||
extract_usings(src) {
|
||||
if src == null { me.usings_json = "[]" return 0 }
|
||||
local n = src.length()
|
||||
local i = 0
|
||||
local first = 1
|
||||
local out = "["
|
||||
loop(i < n) {
|
||||
// read a line
|
||||
local j = i
|
||||
loop(j < n && src.substring(j, j+1) != "\n") { j = j + 1 }
|
||||
local line = src.substring(i, j)
|
||||
// process
|
||||
local k = 0
|
||||
loop(k < line.length() && (line.substring(k,k+1) == " " || line.substring(k,k+1) == "\t")) { k = k + 1 }
|
||||
if me.starts_with(line, k, "using ") == 1 {
|
||||
local rest = me.trim(line.substring(k + 6, line.length()))
|
||||
// split on ' as '
|
||||
local as_pos = me.index_of(rest, 0, " as ")
|
||||
local target = rest
|
||||
local alias = null
|
||||
if as_pos >= 0 {
|
||||
target = me.trim(rest.substring(0, as_pos))
|
||||
alias = me.trim(rest.substring(as_pos + 4, rest.length()))
|
||||
}
|
||||
// path or namespace
|
||||
local is_path = 0
|
||||
if target.length() > 0 {
|
||||
if me.starts_with(target, 0, "\"") == 1 { is_path = 1 }
|
||||
if me.starts_with(target, 0, "./") == 1 { is_path = 1 }
|
||||
if me.starts_with(target, 0, "/") == 1 { is_path = 1 }
|
||||
if target.length() >= 6 && me.starts_with(target, target.length()-6, ".nyash") == 1 { is_path = 1 }
|
||||
}
|
||||
local name = ""
|
||||
local path = null
|
||||
if is_path == 1 {
|
||||
// trim quotes
|
||||
if me.starts_with(target, 0, "\"") == 1 {
|
||||
target = target.substring(1, target.length())
|
||||
if target.length() > 0 && target.substring(target.length()-1, target.length()) == "\"" {
|
||||
target = target.substring(0, target.length()-1)
|
||||
}
|
||||
}
|
||||
path = target
|
||||
if alias != null { name = alias } else {
|
||||
// derive from basename
|
||||
local p = target
|
||||
// find last '/'
|
||||
local idx = -1
|
||||
local t = 0
|
||||
loop(t < p.length()) { if p.substring(t,t+1) == "/" { idx = t } t = t + 1 }
|
||||
if idx >= 0 { p = p.substring(idx+1, p.length()) }
|
||||
// strip .nyash
|
||||
if p.length() > 6 && me.starts_with(p, p.length()-6, ".nyash") == 1 { p = p.substring(0, p.length()-6) }
|
||||
name = p
|
||||
}
|
||||
} else {
|
||||
name = target
|
||||
}
|
||||
// append JSON entry
|
||||
if first == 0 { out = out + "," } else { first = 0 }
|
||||
out = out + "{\"name\":\"" + me.esc_json(name) + "\""
|
||||
if path != null { out = out + ",\"path\":\"" + me.esc_json(path) + "\"" }
|
||||
out = out + "}"
|
||||
}
|
||||
i = j + 1
|
||||
}
|
||||
out = out + "]"
|
||||
me.usings_json = out
|
||||
return 0
|
||||
}
|
||||
get_usings_json() { return me.usings_json }
|
||||
|
||||
to_int(s) { local n = s.length() if n == 0 { return 0 } local i = 0 local acc = 0 loop(i < n) { local d = s.substring(i, i+1) local dv = 0 if d == "1" { dv = 1 } else { if d == "2" { dv = 2 } else { if d == "3" { dv = 3 } else { if d == "4" { dv = 4 } else { if d == "5" { dv = 5 } else { if d == "6" { dv = 6 } else { if d == "7" { dv = 7 } else { if d == "8" { dv = 8 } else { if d == "9" { dv = 9 } } } } } } } } } acc = acc * 10 + dv i = i + 1 } return acc }
|
||||
|
||||
skip_ws(src, i) { if src == null { return i } local n = src.length() local cont = 1 local guard = 0 local max = 100000 loop(cont == 1) { if guard > max { return i } 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 }
|
||||
// identifiers/strings not required for Stage‑1 beyond string literal parse above
|
||||
|
||||
// using metadata omitted in Stage‑1
|
||||
|
||||
parse_number2(src, i) { local n = src.length() local j = i local cont = 1 local guard = 0 local max = 100000 loop(cont == 1) { if guard > max { cont = 0 } else { guard = guard + 1 if j < n { if me.is_digit(src.substring(j, j+1)) { j = j + 1 } else { cont = 0 } } else { cont = 0 } } } local s = src.substring(i, j) me.gpos_set(j) return "{\"type\":\"Int\",\"value\":" + s + "}" }
|
||||
parse_string2(src, i) { local n = src.length() local j = i + 1 local out = "" local guard = 0 local max = 200000 loop(j < n) { if guard > max { break } guard = guard + 1 local ch = src.substring(j, j+1) if ch == "\"" { j = j + 1 me.gpos_set(j) return "{\"type\":\"Str\",\"value\":\"" + me.esc_json(out) + "\"}" } if ch == "\\" && j + 1 < n { local nx = src.substring(j+1, j+2) if nx == "\"" { out = out + "\"" j = j + 2 } else { if nx == "\\" { out = out + "\\" j = j + 2 } else { if nx == "n" { out = out + "\n" j = j + 2 } else { if nx == "r" { out = out + "\r" j = j + 2 } else { if nx == "t" { out = out + "\t" j = j + 2 } else { if nx == "u" && j + 5 < n { out = out + src.substring(j, j+6) j = j + 6 } else { out = out + nx j = j + 2 } } } } } } } else { out = out + ch j = j + 1 } } me.gpos_set(j) return "{\"type\":\"Str\",\"value\":\"" + me.esc_json(out) + "\"}" }
|
||||
|
||||
parse_factor2(src, i) {
|
||||
local j = me.skip_ws(src, i)
|
||||
if j >= src.length() { me.gpos_set(j) return "{\"type\":\"Int\",\"value\":0}" }
|
||||
if me.starts_with_kw(src, j, "true") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Bool\",\"value\":true}" }
|
||||
if me.starts_with_kw(src, j, "false") == 1 { me.gpos_set(j + 5) return "{\"type\":\"Bool\",\"value\":false}" }
|
||||
if me.starts_with_kw(src, j, "null") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Null\"}" }
|
||||
// Peek expression: peek <expr> { "label" => <expr>, ..., else => <expr> }
|
||||
if me.starts_with_kw(src, j, "peek") == 1 {
|
||||
j = j + 4
|
||||
j = me.skip_ws(src, j)
|
||||
// scrutinee expression
|
||||
local scr = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == "{" { j = j + 1 } // enter arms block
|
||||
j = me.skip_ws(src, j)
|
||||
local arms_json = "["
|
||||
local first_arm = 1
|
||||
local else_json = null
|
||||
local n = src.length()
|
||||
local contp = 1
|
||||
local guardp = 0
|
||||
local maxp = 400000
|
||||
loop(contp == 1) {
|
||||
if guardp > maxp { contp = 0 } else { guardp = guardp + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
if j >= n { contp = 0 } else {
|
||||
if src.substring(j, j+1) == "}" {
|
||||
j = j + 1
|
||||
contp = 0
|
||||
} else {
|
||||
// else arm or labeled arm
|
||||
if me.starts_with_kw(src, j, "else") == 1 {
|
||||
j = j + 4
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+2) == "=>" { j = j + 2 }
|
||||
j = me.skip_ws(src, j)
|
||||
// else body may be a block or bare expr
|
||||
if src.substring(j, j+1) == "{" {
|
||||
j = j + 1
|
||||
j = me.skip_ws(src, j)
|
||||
else_json = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == "}" { j = j + 1 }
|
||||
} else {
|
||||
else_json = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
}
|
||||
// optional separator/newline tolerated; continue until '}'
|
||||
} else {
|
||||
// labeled arm: string literal label
|
||||
if src.substring(j, j+1) != "\"" {
|
||||
// degrade safely to avoid infinite loop
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
local label_raw = me.read_string_lit(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+2) == "=>" { j = j + 2 }
|
||||
j = me.skip_ws(src, j)
|
||||
// arm expr: block or bare expr
|
||||
local expr_json = "{\"type\":\"Int\",\"value\":0}"
|
||||
if src.substring(j, j+1) == "{" {
|
||||
j = j + 1
|
||||
j = me.skip_ws(src, j)
|
||||
expr_json = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == "}" { j = j + 1 }
|
||||
} else {
|
||||
expr_json = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
}
|
||||
local arm_json = "{\"label\":\"" + me.esc_json(label_raw) + "\",\"expr\":" + expr_json + "}"
|
||||
if first_arm == 1 { arms_json = arms_json + arm_json first_arm = 0 } else { arms_json = arms_json + "," + arm_json }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
arms_json = arms_json + "]"
|
||||
if else_json == null { else_json = "{\"type\":\"Null\"}" }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Peek\",\"scrutinee\":" + scr + ",\"arms\":" + arms_json + ",\"else\":" + else_json + "}"
|
||||
}
|
||||
local ch = src.substring(j, j+1)
|
||||
// Parenthesized
|
||||
if ch == "(" {
|
||||
local inner = me.parse_expr2(src, j + 1)
|
||||
local k = me.gpos_get()
|
||||
k = me.skip_ws(src, k)
|
||||
if src.substring(k, k+1) == ")" { k = k + 1 }
|
||||
me.gpos_set(k)
|
||||
return inner
|
||||
}
|
||||
// String literal
|
||||
if ch == "\"" { return me.parse_string2(src, j) }
|
||||
// Map literal: {"k": v, ...} (string keys only) → Call{name:"map.of", args:[Str(k1), v1, Str(k2), v2, ...]}
|
||||
if ch == "{" {
|
||||
local n = src.length()
|
||||
j = j + 1
|
||||
local out = "["
|
||||
local first = 1
|
||||
local cont = 1
|
||||
local guard = 0
|
||||
local max = 400000
|
||||
loop(cont == 1) {
|
||||
if guard > max { cont = 0 } else { guard = guard + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
if j >= n { cont = 0 } else {
|
||||
if src.substring(j, j+1) == "}" { j = j + 1 cont = 0 } else {
|
||||
// key (string only for Stage-2)
|
||||
if src.substring(j, j+1) != "\"" {
|
||||
// degrade by skipping one char to avoid infinite loop
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
local key_raw = me.read_string_lit(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == ":" { j = j + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
local val_json = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
local key_json = "{\"type\":\"Str\",\"value\":\"" + me.esc_json(key_raw) + "\"}"
|
||||
if first == 1 { out = out + key_json + "," + val_json first = 0 } else { out = out + "," + key_json + "," + val_json }
|
||||
// optional comma
|
||||
local before2 = j
|
||||
j = me.skip_ws(src, j)
|
||||
if j < n && src.substring(j, j+1) == "," { j = j + 1 }
|
||||
// progress guard (in case of malformed input)
|
||||
if j <= before2 { if j < n { j = j + 1 } else { j = n } }
|
||||
}
|
||||
}
|
||||
}
|
||||
out = out + "]"
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Call\",\"name\":\"map.of\",\"args\":" + out + "}"
|
||||
}
|
||||
// Array literal: [e1, e2, ...] → Call{name:"array.of", args:[...]}
|
||||
if ch == "[" {
|
||||
local n = src.length()
|
||||
j = j + 1
|
||||
local out = "["
|
||||
local first = 1
|
||||
local cont = 1
|
||||
local guard = 0
|
||||
local max = 400000
|
||||
loop(cont == 1) {
|
||||
if guard > max { cont = 0 } else { guard = guard + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
if j >= n { cont = 0 } else {
|
||||
if src.substring(j, j+1) == "]" { j = j + 1 cont = 0 } else {
|
||||
local before = j
|
||||
local ej = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
if first == 1 { out = out + ej first = 0 } else { out = out + "," + ej }
|
||||
// optional comma+whitespace
|
||||
local before2 = j
|
||||
j = me.skip_ws(src, j)
|
||||
if j < n && src.substring(j, j+1) == "," { j = j + 1 }
|
||||
// progress guard
|
||||
if j <= before { if j < n { j = j + 1 } else { j = n } }
|
||||
}
|
||||
}
|
||||
}
|
||||
out = out + "]"
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Call\",\"name\":\"array.of\",\"args\":" + out + "}"
|
||||
}
|
||||
// true/false
|
||||
if me.starts_with_kw(src, j, "true") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Bool\",\"value\":true}" }
|
||||
if me.starts_with_kw(src, j, "false") == 1 { me.gpos_set(j + 5) return "{\"type\":\"Bool\",\"value\":false}" }
|
||||
// new Class(args)
|
||||
if me.starts_with_kw(src, j, "new") == 1 {
|
||||
local p = me.skip_ws(src, j + 3)
|
||||
local idp = me.read_ident2(src, p)
|
||||
local at = idp.lastIndexOf("@")
|
||||
local cls = idp.substring(0, at)
|
||||
local k = me.to_int(idp.substring(at+1, idp.length()))
|
||||
k = me.skip_ws(src, k)
|
||||
if src.substring(k, k+1) == "(" { k = k + 1 }
|
||||
local args_and_pos = me.parse_args2(src, k)
|
||||
local at2 = args_and_pos.lastIndexOf("@")
|
||||
local args_json = args_and_pos.substring(0, at2)
|
||||
k = me.to_int(args_and_pos.substring(at2+1, args_and_pos.length()))
|
||||
k = me.skip_ws(src, k)
|
||||
if src.substring(k, k+1) == ")" { k = k + 1 }
|
||||
me.gpos_set(k)
|
||||
return "{\"type\":\"New\",\"class\":\"" + cls + "\",\"args\":" + args_json + "}"
|
||||
}
|
||||
// Identifier / Call / Method chain
|
||||
if me.is_alpha(ch) {
|
||||
local idp = me.read_ident2(src, j)
|
||||
local at = idp.lastIndexOf("@")
|
||||
local name = idp.substring(0, at)
|
||||
local k = me.to_int(idp.substring(at+1, idp.length()))
|
||||
local node = "{\"type\":\"Var\",\"name\":\"" + name + "\"}"
|
||||
local cont2 = 1
|
||||
loop(cont2 == 1) {
|
||||
k = me.skip_ws(src, k)
|
||||
local tch = src.substring(k, k+1)
|
||||
if tch == "(" {
|
||||
k = k + 1
|
||||
local args_and_pos = me.parse_args2(src, k)
|
||||
local at2 = args_and_pos.lastIndexOf("@")
|
||||
local args_json = args_and_pos.substring(0, at2)
|
||||
k = me.to_int(args_and_pos.substring(at2+1, args_and_pos.length()))
|
||||
k = me.skip_ws(src, k)
|
||||
if src.substring(k, k+1) == ")" { k = k + 1 }
|
||||
node = "{\"type\":\"Call\",\"name\":\"" + name + "\",\"args\":" + args_json + "}"
|
||||
} else {
|
||||
if tch == "." {
|
||||
k = k + 1
|
||||
k = me.skip_ws(src, k)
|
||||
local midp = me.read_ident2(src, k)
|
||||
local at3 = midp.lastIndexOf("@")
|
||||
local mname = midp.substring(0, at3)
|
||||
k = me.to_int(midp.substring(at3+1, midp.length()))
|
||||
k = me.skip_ws(src, k)
|
||||
if src.substring(k, k+1) == "(" { k = k + 1 }
|
||||
local args2 = me.parse_args2(src, k)
|
||||
local at4 = args2.lastIndexOf("@")
|
||||
local args_json2 = args2.substring(0, at4)
|
||||
k = me.to_int(args2.substring(at4+1, args2.length()))
|
||||
k = me.skip_ws(src, k)
|
||||
if src.substring(k, k+1) == ")" { k = k + 1 }
|
||||
node = "{\"type\":\"Method\",\"recv\":" + node + ",\"method\":\"" + mname + "\",\"args\":" + args_json2 + "}"
|
||||
} else { cont2 = 0 }
|
||||
}
|
||||
}
|
||||
me.gpos_set(k)
|
||||
return node
|
||||
}
|
||||
// Fallback: number
|
||||
return me.parse_number2(src, j)
|
||||
}
|
||||
// unary minus binds tighter than * /
|
||||
parse_unary2(src, i) {
|
||||
local j = me.skip_ws(src, i)
|
||||
if src.substring(j, j+1) == "-" {
|
||||
local rhs = me.parse_factor2(src, j + 1)
|
||||
j = me.gpos_get()
|
||||
local zero = "{\"type\":\"Int\",\"value\":0}"
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Binary\",\"op\":\"-\",\"lhs\":" + zero + ",\"rhs\":" + rhs + "}"
|
||||
}
|
||||
return me.parse_factor2(src, j)
|
||||
}
|
||||
parse_term2(src, i) { local lhs = me.parse_unary2(src, i) local j = me.gpos_get() local cont = 1 loop(cont == 1) { j = me.skip_ws(src, j) if j >= src.length() { cont = 0 } else { local op = src.substring(j, j+1) if op != "*" && op != "/" { cont = 0 } else { local rhs = me.parse_unary2(src, j+1) j = me.gpos_get() lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" } } } me.gpos_set(j) return lhs }
|
||||
parse_sum2(src, i) { local lhs = me.parse_term2(src, i) local j = me.gpos_get() local cont = 1 loop(cont == 1) { j = me.skip_ws(src, j) if j >= src.length() { cont = 0 } else { local op = src.substring(j, j+1) if op != "+" && op != "-" { cont = 0 } else { local rhs = me.parse_term2(src, j+1) j = me.gpos_get() lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" } } } me.gpos_set(j) return lhs }
|
||||
parse_compare2(src, i) { local lhs = me.parse_sum2(src, i) local j = me.gpos_get() j = me.skip_ws(src, j) local two = src.substring(j, j+2) local one = src.substring(j, j+1) local op = "" if two == "==" || two == "!=" || two == "<=" || two == ">=" { op = two j = j + 2 } else { if one == "<" || one == ">" { op = one j = j + 1 } } if op == "" { me.gpos_set(j) return lhs } local rhs = me.parse_sum2(src, j) j = me.gpos_get() me.gpos_set(j) return "{\"type\":\"Compare\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}" }
|
||||
parse_expr2(src, i) {
|
||||
local lhs = me.parse_compare2(src, i)
|
||||
local j = me.gpos_get()
|
||||
local cont = 1
|
||||
loop(cont == 1) {
|
||||
j = me.skip_ws(src, j)
|
||||
local two = src.substring(j, j+2)
|
||||
if two != "&&" && two != "||" { cont = 0 } else {
|
||||
local rhs = me.parse_compare2(src, j+2)
|
||||
j = me.gpos_get()
|
||||
lhs = "{\"type\":\"Logical\",\"op\":\"" + two + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
|
||||
}
|
||||
}
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == "?" {
|
||||
j = j + 1
|
||||
j = me.skip_ws(src, j)
|
||||
local then_expr = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == ":" { j = j + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
local else_expr = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
if else_expr.length() == 0 { else_expr = "{\"type\":\"Int\",\"value\":0}" }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Ternary\",\"cond\":" + lhs + ",\"then\":" + then_expr + ",\"else\":" + else_expr + "}"
|
||||
}
|
||||
me.gpos_set(j)
|
||||
return lhs
|
||||
}
|
||||
parse_args2(src, i) {
|
||||
local j = me.skip_ws(src, i)
|
||||
local n = src.length()
|
||||
local out = "["
|
||||
j = me.skip_ws(src, j)
|
||||
if j < n && src.substring(j, j+1) == ")" { return "[]@" + me.i2s(j) }
|
||||
// first argument
|
||||
local e = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
out = out + e
|
||||
// subsequent arguments with guard
|
||||
local cont_args = 1
|
||||
local guard = 0
|
||||
local max = 100000
|
||||
loop(cont_args == 1) {
|
||||
if guard > max { cont_args = 0 } else { guard = guard + 1 }
|
||||
local before = j
|
||||
j = me.skip_ws(src, j)
|
||||
if j < n && src.substring(j, j+1) == "," {
|
||||
j = j + 1
|
||||
j = me.skip_ws(src, j)
|
||||
e = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
out = out + "," + e
|
||||
} else { cont_args = 0 }
|
||||
if j == before { cont_args = 0 }
|
||||
}
|
||||
out = out + "]"
|
||||
return out + "@" + me.i2s(j)
|
||||
}
|
||||
parse_stmt2(src, i) {
|
||||
local j = me.skip_ws(src, i)
|
||||
local stmt_start = j
|
||||
if me.starts_with_kw(src, j, "using") == 1 {
|
||||
j = j + 5
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == "\"" {
|
||||
local p = me.read_string_lit(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
local alias = null
|
||||
if me.starts_with_kw(src, j, "as") == 1 { j = j + 2 j = me.skip_ws(src, j) local idp = me.read_ident2(src, j) local at = idp.lastIndexOf("@") alias = idp.substring(0, at) j = me.to_int(idp.substring(at+1, idp.length())) }
|
||||
me.add_using("path", p, alias)
|
||||
} else {
|
||||
if me.is_alpha(src.substring(j, j+1)) {
|
||||
local idp = me.read_ident2(src, j)
|
||||
local at = idp.lastIndexOf("@")
|
||||
local name = idp.substring(0, at)
|
||||
j = me.to_int(idp.substring(at+1, idp.length()))
|
||||
local cont = 1
|
||||
loop(cont == 1) {
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == "." { j = j + 1 j = me.skip_ws(src, j) idp = me.read_ident2(src, j) at = idp.lastIndexOf("@") name = name + "." + idp.substring(0, at) j = me.to_int(idp.substring(at+1, idp.length())) } else { cont = 0 }
|
||||
}
|
||||
j = me.skip_ws(src, j)
|
||||
local alias2 = null
|
||||
if me.starts_with_kw(src, j, "as") == 1 { j = j + 2 j = me.skip_ws(src, j) idp = me.read_ident2(src, j) at = idp.lastIndexOf("@") alias2 = idp.substring(0, at) j = me.to_int(idp.substring(at+1, idp.length())) }
|
||||
me.add_using("ns", name, alias2)
|
||||
}
|
||||
}
|
||||
// ensure progress
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return ""
|
||||
}
|
||||
// simple assignment: IDENT '=' expr ; → JSON v0 Local{name, expr} (Stage‑2 uses Local for updates)
|
||||
if j < src.length() && me.is_alpha(src.substring(j, j+1)) {
|
||||
local idp0 = me.read_ident2(src, j)
|
||||
local at0 = idp0.lastIndexOf("@")
|
||||
if at0 > 0 {
|
||||
local name0 = idp0.substring(0, at0)
|
||||
local k0 = me.to_int(idp0.substring(at0+1, idp0.length()))
|
||||
k0 = me.skip_ws(src, k0)
|
||||
if k0 < src.length() && src.substring(k0, k0+1) == "=" {
|
||||
local eq_two = "="
|
||||
if k0 + 1 < src.length() { eq_two = src.substring(k0, k0+2) }
|
||||
if eq_two != "==" {
|
||||
k0 = k0 + 1
|
||||
k0 = me.skip_ws(src, k0)
|
||||
local default_local = "{\"type\":\"Int\",\"value\":0}"
|
||||
local expr_json0 = default_local
|
||||
local end_pos0 = k0
|
||||
if k0 < src.length() {
|
||||
local ahead = src.substring(k0, k0+1)
|
||||
if ahead != "}" && ahead != ";" {
|
||||
expr_json0 = me.parse_expr2(src, k0)
|
||||
end_pos0 = me.gpos_get()
|
||||
}
|
||||
}
|
||||
k0 = end_pos0
|
||||
if k0 <= stmt_start { if k0 < src.length() { k0 = k0 + 1 } else { k0 = src.length() } }
|
||||
me.gpos_set(k0)
|
||||
return "{\"type\":\"Local\",\"name\":\"" + name0 + "\",\"expr\":" + expr_json0 + "}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if me.starts_with_kw(src, j, "return") == 1 {
|
||||
j = j + 6
|
||||
j = me.skip_ws(src, j)
|
||||
local default_ret = "{\"type\":\"Int\",\"value\":0}"
|
||||
local expr_json_ret = default_ret
|
||||
local end_pos_ret = j
|
||||
if j < src.length() {
|
||||
local ahead_ret = src.substring(j, j+1)
|
||||
if ahead_ret != "}" && ahead_ret != ";" {
|
||||
expr_json_ret = me.parse_expr2(src, j)
|
||||
end_pos_ret = me.gpos_get()
|
||||
}
|
||||
}
|
||||
j = end_pos_ret
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Return\",\"expr\":" + expr_json_ret + "}"
|
||||
}
|
||||
if me.starts_with_kw(src, j, "local") == 1 {
|
||||
j = j + 5
|
||||
j = me.skip_ws(src, j)
|
||||
local idp = me.read_ident2(src, j)
|
||||
local at = idp.lastIndexOf("@")
|
||||
local name = idp.substring(0, at)
|
||||
j = me.to_int(idp.substring(at+1, idp.length()))
|
||||
j = me.skip_ws(src, j)
|
||||
if j < src.length() && src.substring(j, j+1) == "=" { j = j + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
local default_local = "{\"type\":\"Int\",\"value\":0}"
|
||||
local expr_json_local = default_local
|
||||
local end_pos_local = j
|
||||
if j < src.length() {
|
||||
local ahead_local = src.substring(j, j+1)
|
||||
if ahead_local != "}" && ahead_local != ";" {
|
||||
expr_json_local = me.parse_expr2(src, j)
|
||||
end_pos_local = me.gpos_get()
|
||||
}
|
||||
}
|
||||
j = end_pos_local
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Local\",\"name\":\"" + name + "\",\"expr\":" + expr_json_local + "}"
|
||||
}
|
||||
if me.starts_with_kw(src, j, "if") == 1 {
|
||||
j = j + 2
|
||||
j = me.skip_ws(src, j)
|
||||
local paren = 0
|
||||
if src.substring(j, j+1) == "(" { paren = 1 j = j + 1 }
|
||||
local cond = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
if paren == 1 { j = me.skip_ws(src, j) if src.substring(j, j+1) == ")" { j = j + 1 } }
|
||||
j = me.skip_ws(src, j)
|
||||
local then_res = me.parse_block2(src, j)
|
||||
local at1 = then_res.lastIndexOf("@")
|
||||
local then_json = then_res.substring(0, at1)
|
||||
j = me.to_int(then_res.substring(at1+1, then_res.length()))
|
||||
j = me.skip_ws(src, j)
|
||||
local else_json = null
|
||||
if me.starts_with_kw(src, j, "else") == 1 { j = j + 4 j = me.skip_ws(src, j) local else_res = me.parse_block2(src, j) local at2 = else_res.lastIndexOf("@") else_json = else_res.substring(0, at2) j = me.to_int(else_res.substring(at2+1, else_res.length())) }
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
if else_json == null { return "{\"type\":\"If\",\"cond\":" + cond + ",\"then\":" + then_json + "}" } else { return "{\"type\":\"If\",\"cond\":" + cond + ",\"then\":" + then_json + ",\"else\":" + else_json + "}" }
|
||||
}
|
||||
if me.starts_with_kw(src, j, "loop") == 1 {
|
||||
j = j + 4
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == "(" { j = j + 1 }
|
||||
local cond = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
j = me.skip_ws(src, j)
|
||||
if src.substring(j, j+1) == ")" { j = j + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
local body_res = me.parse_block2(src, j)
|
||||
local at3 = body_res.lastIndexOf("@")
|
||||
local body_json = body_res.substring(0, at3)
|
||||
j = me.to_int(body_res.substring(at3+1, body_res.length()))
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Loop\",\"cond\":" + cond + ",\"body\":" + body_json + "}"
|
||||
}
|
||||
// Stage-3 acceptance (syntax only): break / continue → no-op expression
|
||||
if me.starts_with_kw(src, j, "break") == 1 {
|
||||
j = j + 5
|
||||
if me.stage3_enabled() == 1 {
|
||||
j = me.skip_ws(src, j)
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Break\"}"
|
||||
}
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
|
||||
}
|
||||
if me.starts_with_kw(src, j, "continue") == 1 {
|
||||
j = j + 8
|
||||
if me.stage3_enabled() == 1 {
|
||||
j = me.skip_ws(src, j)
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Continue\"}"
|
||||
}
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
|
||||
}
|
||||
// Stage-3 acceptance: throw expr → degrade to Expr(expr)
|
||||
if me.starts_with_kw(src, j, "throw") == 1 {
|
||||
j = j + 5
|
||||
j = me.skip_ws(src, j)
|
||||
local e_throw = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
if me.stage3_enabled() == 1 {
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Throw\",\"expr\":" + e_throw + "}"
|
||||
}
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Expr\",\"expr\":" + e_throw + "}"
|
||||
}
|
||||
// Stage-3 acceptance: try { ... } (catch ...)* (cleanup { ... })? → degrade to no-op (syntax only)
|
||||
if me.starts_with_kw(src, j, "try") == 1 {
|
||||
j = j + 3
|
||||
j = me.skip_ws(src, j)
|
||||
// parse try block
|
||||
local try_res = me.parse_block2(src, j)
|
||||
local at_t = try_res.lastIndexOf("@")
|
||||
local try_json = try_res.substring(0, at_t)
|
||||
j = me.to_int(try_res.substring(at_t+1, try_res.length()))
|
||||
local catches_json = "["
|
||||
local catch_first = 1
|
||||
// zero or more catch
|
||||
local guard_ct = 0
|
||||
local max_ct = 100
|
||||
local cont_ct = 1
|
||||
loop(cont_ct == 1) {
|
||||
if guard_ct > max_ct { cont_ct = 0 } else { guard_ct = guard_ct + 1 }
|
||||
j = me.skip_ws(src, j)
|
||||
if me.starts_with_kw(src, j, "catch") == 1 {
|
||||
j = j + 5
|
||||
j = me.skip_ws(src, j)
|
||||
local catch_type = null
|
||||
local catch_param = null
|
||||
if src.substring(j, j+1) == "(" { j = j + 1 j = me.skip_ws(src, j)
|
||||
// optional type + name
|
||||
if me.is_alpha(src.substring(j, j+1)) {
|
||||
local id1 = me.read_ident2(src, j)
|
||||
local at1 = id1.lastIndexOf("@")
|
||||
catch_type = id1.substring(0, at1)
|
||||
j = me.to_int(id1.substring(at1+1, id1.length()))
|
||||
j = me.skip_ws(src, j)
|
||||
}
|
||||
if me.is_alpha(src.substring(j, j+1)) {
|
||||
local id2 = me.read_ident2(src, j)
|
||||
local at2 = id2.lastIndexOf("@")
|
||||
catch_param = id2.substring(0, at2)
|
||||
j = me.to_int(id2.substring(at2+1, id2.length()))
|
||||
j = me.skip_ws(src, j)
|
||||
}
|
||||
if src.substring(j, j+1) == ")" { j = j + 1 }
|
||||
}
|
||||
j = me.skip_ws(src, j)
|
||||
// catch body
|
||||
local c_res = me.parse_block2(src, j)
|
||||
local atc = c_res.lastIndexOf("@")
|
||||
j = me.to_int(c_res.substring(atc+1, c_res.length()))
|
||||
if me.stage3_enabled() == 1 {
|
||||
local entry = "{"
|
||||
local wrote = 0
|
||||
if catch_param != null && catch_param.length() > 0 { entry = entry + "\"param\":\"" + me.esc_json(catch_param) + "\"" wrote = 1 }
|
||||
if catch_type != null && catch_type.length() > 0 { if wrote == 1 { entry = entry + "," } entry = entry + "\"typeHint\":\"" + me.esc_json(catch_type) + "\"" wrote = 1 }
|
||||
local body_json = c_res.substring(0, atc)
|
||||
if wrote == 1 { entry = entry + "," }
|
||||
entry = entry + "\"body\":" + body_json + "}"
|
||||
if catch_first == 0 { catches_json = catches_json + "," + entry } else { catches_json = catches_json + entry catch_first = 0 }
|
||||
}
|
||||
} else { cont_ct = 0 }
|
||||
}
|
||||
catches_json = catches_json + "]"
|
||||
// optional cleanup
|
||||
j = me.skip_ws(src, j)
|
||||
local finally_json = null
|
||||
if me.starts_with_kw(src, j, "cleanup") == 1 {
|
||||
j = j + 7
|
||||
j = me.skip_ws(src, j)
|
||||
local f_res = me.parse_block2(src, j)
|
||||
local atf = f_res.lastIndexOf("@")
|
||||
j = me.to_int(f_res.substring(atf+1, f_res.length()))
|
||||
finally_json = f_res.substring(0, atf)
|
||||
}
|
||||
if me.stage3_enabled() == 1 {
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
local node = "{\"type\":\"Try\",\"try\":" + try_json + ",\"catches\":" + catches_json
|
||||
if finally_json != null { node = node + ",\"finally\":" + finally_json }
|
||||
node = node + "}"
|
||||
return node
|
||||
}
|
||||
if j <= stmt_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
|
||||
}
|
||||
// Fallback: expression or unknown token — ensure progress even on malformed input
|
||||
local expr_start = j
|
||||
local e = me.parse_expr2(src, j)
|
||||
j = me.gpos_get()
|
||||
if j <= expr_start { if j < src.length() { j = j + 1 } else { j = src.length() } }
|
||||
me.gpos_set(j)
|
||||
return "{\"type\":\"Expr\",\"expr\":" + e + "}"
|
||||
}
|
||||
parse_block2(src, i) {
|
||||
local j = me.skip_ws(src, i)
|
||||
if src.substring(j, j+1) != "{" { return "[]@" + me.i2s(j) }
|
||||
j = j + 1
|
||||
local body = "["
|
||||
local first = 1
|
||||
local cont_block = 1
|
||||
loop(cont_block == 1) {
|
||||
j = me.skip_ws(src, j)
|
||||
if j >= src.length() { cont_block = 0 } else {
|
||||
if src.substring(j, j+1) == "}" { j = j + 1 cont_block = 0 } else {
|
||||
local start_j = j
|
||||
local s = me.parse_stmt2(src, j)
|
||||
j = me.gpos_get()
|
||||
// Progress guard: ensure forward movement to avoid infinite loop on malformed input
|
||||
if j <= start_j {
|
||||
if j < src.length() { j = j + 1 } else { j = src.length() }
|
||||
me.gpos_set(j)
|
||||
}
|
||||
// consume optional semicolons (ASI minimal)
|
||||
local done = 0
|
||||
local guard = 0
|
||||
local max = 100000
|
||||
loop(done == 0) {
|
||||
if guard > max { done = 1 } else { guard = guard + 1 }
|
||||
local before = j
|
||||
j = me.skip_ws(src, j)
|
||||
if j < src.length() && src.substring(j, j+1) == ";" { j = j + 1 } else { done = 1 }
|
||||
if j == before { done = 1 }
|
||||
}
|
||||
if s.length() > 0 { if first == 1 { body = body + s first = 0 } else { body = body + "," + s } }
|
||||
}
|
||||
}
|
||||
}
|
||||
body = body + "]"
|
||||
return body + "@" + me.i2s(j)
|
||||
}
|
||||
parse_program2(src) {
|
||||
local i = me.skip_ws(src, 0)
|
||||
local body = "["
|
||||
local first = 1
|
||||
local cont_prog = 1
|
||||
loop(cont_prog == 1) {
|
||||
i = me.skip_ws(src, i)
|
||||
if i >= src.length() { cont_prog = 0 } else {
|
||||
local start_i = i
|
||||
local s = me.parse_stmt2(src, i)
|
||||
i = me.gpos_get()
|
||||
// Progress guard: ensure forward movement to avoid infinite loop on malformed input
|
||||
if i <= start_i {
|
||||
if i < src.length() { i = i + 1 } else { i = src.length() }
|
||||
me.gpos_set(i)
|
||||
}
|
||||
// consume optional semicolons between top-level statements
|
||||
local done2 = 0
|
||||
local guard2 = 0
|
||||
local max2 = 100000
|
||||
loop(done2 == 0) {
|
||||
if guard2 > max2 { done2 = 1 } else { guard2 = guard2 + 1 }
|
||||
local before2 = i
|
||||
i = me.skip_ws(src, i)
|
||||
if i < src.length() && src.substring(i, i+1) == ";" { i = i + 1 } else { done2 = 1 }
|
||||
if i == before2 { done2 = 1 }
|
||||
}
|
||||
if s.length() > 0 { if first == 1 { body = body + s first = 0 } else { body = body + "," + s } }
|
||||
}
|
||||
}
|
||||
body = body + "]"
|
||||
return "{\"version\":0,\"kind\":\"Program\",\"body\":" + body + "}"
|
||||
}
|
||||
}
|
||||
|
||||
static box ParserStub { main(args) { return 0 } }
|
||||
@ -1,158 +0,0 @@
|
||||
// Selfhost Compiler MVP (Phase 15.3)
|
||||
// Reads tmp/ny_parser_input.ny and prints a minimal JSON v0 program.
|
||||
// Components are split under boxes/ and included here.
|
||||
|
||||
// Prefer using for module declaration (Runner strips and registers)
|
||||
using "apps/selfhost-compiler/boxes/debug_box.nyash" as DebugBoxMod
|
||||
using "apps/selfhost-compiler/boxes/parser_box.nyash" as ParserBoxMod
|
||||
using "apps/selfhost-compiler/boxes/emitter_box.nyash" as EmitterBoxMod
|
||||
using "apps/selfhost-compiler/boxes/mir_emitter_box.nyash" as MirEmitterBoxMod
|
||||
|
||||
// Transitional: keep include for Phase-15 compatibility
|
||||
using "apps/selfhost-compiler/boxes/debug_box.nyash"
|
||||
using "apps/selfhost-compiler/boxes/parser_box.nyash"
|
||||
using "apps/selfhost-compiler/boxes/emitter_box.nyash"
|
||||
using "apps/selfhost-compiler/boxes/mir_emitter_box.nyash"
|
||||
// Prepass libs (ScopeBox/LoopForm)
|
||||
using "apps/lib/scopebox_inject.nyash"
|
||||
using "apps/lib/loopform_normalize.nyash"
|
||||
|
||||
static box Main {
|
||||
// ---- IO helper ----
|
||||
read_all(path) {
|
||||
local fb = new FileBox()
|
||||
fb.open(path, "r")
|
||||
local s = fb.read()
|
||||
fb.close()
|
||||
if s == null { return "return 1+2*3" }
|
||||
return s
|
||||
}
|
||||
|
||||
// ---- JSON helpers ----
|
||||
esc_json(s) {
|
||||
local out = ""
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "\\" { out = out + "\\\\" } else {
|
||||
if ch == "\"" { out = out + "\\\"" } else { out = out + ch }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Parser delegation
|
||||
parse_program(src, stage3_flag) {
|
||||
local parser = new ParserBox()
|
||||
if stage3_flag == 1 { parser.stage3_enable(1) }
|
||||
// Collect using metadata (no-op acceptance in Stage‑15)
|
||||
parser.extract_usings(src)
|
||||
me._usings = parser.get_usings_json()
|
||||
return parser.parse_program2(src)
|
||||
}
|
||||
|
||||
main(args) {
|
||||
// Debug setup
|
||||
me.dbg = new DebugBox()
|
||||
me.dbg.set_enabled(0)
|
||||
|
||||
// Source selection (EXE-first friendly)
|
||||
// - default: safe constant
|
||||
// - positional arg: treat as input file path
|
||||
// - --read-tmp: use tmp/ny_parser_input.ny (requires FileBox plugin)
|
||||
local src = "return 1+2*3"
|
||||
local read_tmp = 0
|
||||
local input_path = null
|
||||
local stage3_mode = 0
|
||||
if args != null {
|
||||
local alen = args.length()
|
||||
local i = 0
|
||||
loop(i < alen) {
|
||||
local a = args.get(i)
|
||||
if a == "--read-tmp" {
|
||||
read_tmp = 1
|
||||
} else {
|
||||
if a == "--min-json" {
|
||||
/* handled later */
|
||||
} else {
|
||||
if a == "--stage3" {
|
||||
stage3_mode = 1
|
||||
} else {
|
||||
if input_path == null { input_path = a }
|
||||
}
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
if input_path != null {
|
||||
// Prefer explicit file when provided (requires FileBox plugin)
|
||||
local s1 = me.read_all(input_path)
|
||||
if s1 != null { src = s1 }
|
||||
} else {
|
||||
if read_tmp == 1 {
|
||||
// Optional: read tmp/ny_parser_input.ny (requires FileBox plugin; do not use in CI)
|
||||
local s2 = me.read_all("tmp/ny_parser_input.ny")
|
||||
if s2 != null { src = s2 }
|
||||
}
|
||||
}
|
||||
|
||||
// Gate: minimal JSON when requested via script arg
|
||||
local min_mode = 0
|
||||
local emit_mir = 0
|
||||
if args != null {
|
||||
local alen = args.length()
|
||||
local i = 0
|
||||
loop(i < alen) {
|
||||
local arg = args.get(i)
|
||||
if arg == "--min-json" { min_mode = 1 }
|
||||
if arg == "--emit-mir" { emit_mir = 1 }
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
local json = null
|
||||
local ast_json = null
|
||||
if min_mode == 1 {
|
||||
ast_json = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}"
|
||||
} else {
|
||||
ast_json = me.parse_program(src, stage3_mode)
|
||||
}
|
||||
|
||||
// Optional prepasses driven by CLI flags (mapped from env by runner)
|
||||
local do_scopebox = 0
|
||||
local do_loopform = 0
|
||||
if args != null {
|
||||
local alen3 = args.length()
|
||||
local i3 = 0
|
||||
loop(i3 < alen3) {
|
||||
local a3 = args.get(i3)
|
||||
if a3 == "--scopebox" { do_scopebox = 1 }
|
||||
if a3 == "--loopform" { do_loopform = 1 }
|
||||
i3 = i3 + 1
|
||||
}
|
||||
}
|
||||
|
||||
if emit_mir == 1 {
|
||||
// Lower minimal AST to MIR JSON (Return(Int) only for MVP)
|
||||
local mir = new MirEmitterBox()
|
||||
local aj = ast_json
|
||||
if do_scopebox == 1 { aj = new ScopeBoxInject().apply(aj) }
|
||||
if do_loopform == 1 { aj = new LoopFormNormalize().apply(aj) }
|
||||
json = mir.emit_mir_min(aj)
|
||||
} else {
|
||||
// Emit Stage‑1 JSON with metadata
|
||||
local emitter = new EmitterBox()
|
||||
local aj2 = ast_json
|
||||
if do_scopebox == 1 { aj2 = new ScopeBoxInject().apply(aj2) }
|
||||
if do_loopform == 1 { aj2 = new LoopFormNormalize().apply(aj2) }
|
||||
json = emitter.emit_program(aj2, me._usings)
|
||||
}
|
||||
|
||||
// Output JSON
|
||||
local console = new ConsoleBox()
|
||||
console.println(json)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
// JSON v0 emitter (MVP placeholder)
|
||||
static box JsonV0Emitter {
|
||||
// Emit a minimal Program{return 0}
|
||||
program_return0() {
|
||||
return "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
static box MirBuilder {
|
||||
// placeholder
|
||||
main(args) { return 0 }
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
static box Optimizer {
|
||||
// placeholder
|
||||
main(args) { return 0 }
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
static box AST {
|
||||
// scaffold for future AST node constructors
|
||||
main(args) { return 0 }
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
static box Lexer {
|
||||
// scaffold for future implementation
|
||||
main(args) { return 0 }
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
static box Parser {
|
||||
// scaffold for future implementation
|
||||
main(args) { return 0 }
|
||||
}
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
Stage‑1 tests (scaffold)
|
||||
|
||||
Add minimal Ny source samples here. Harness TBD.
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
# Ny Parser (v0) — Minimal Nyash-made Parser
|
||||
|
||||
- Scope: integers, + - * /, parentheses, and a single `return` statement.
|
||||
- Output: JSON IR v0 as documented in CURRENT_TASK.md (Program/Return/Int/Binary).
|
||||
|
||||
Usage (Unix)
|
||||
- echo "return 1+2*3" | ./tools/ny_parser_run.sh
|
||||
|
||||
Usage (Windows PowerShell)
|
||||
- Get-Content .\apps\ny-mir-samples\arithmetic.nyash | .\tools\ny_parser_run.ps1
|
||||
|
||||
Notes
|
||||
- This is a minimal educational parser to bootstrap the self-host loop.
|
||||
- Errors print a JSON envelope: {"version":0,"kind":"Error",...}.
|
||||
@ -1,29 +0,0 @@
|
||||
// Entry: read stdin, parse with ParserV0, print JSON IR or error JSON
|
||||
|
||||
using "./apps/selfhost/ny-parser-nyash/parser_minimal.nyash" as ParserMod
|
||||
using "./apps/selfhost/ny-parser-nyash/parser_minimal.nyash"
|
||||
|
||||
static box Main {
|
||||
main(args) {
|
||||
local console = new ConsoleBox()
|
||||
// Read all stdin
|
||||
local buf = ""
|
||||
loop(true) {
|
||||
local line = console.readLine()
|
||||
if line == null { break }
|
||||
buf = buf + line + "\n"
|
||||
}
|
||||
if buf == "" { buf = "return 0\n" }
|
||||
local ir = ParserV0.parse_program(buf)
|
||||
// If already an Error envelope, print as-is
|
||||
local s = ir.as_any().toString()
|
||||
if s.indexOf("\"kind\":\"Error\"") >= 0 {
|
||||
console.log(s)
|
||||
return 1
|
||||
}
|
||||
// Expect MapBox with Program; toJson available on MapBox
|
||||
local json = ir.toJson()
|
||||
console.log(json)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
// Minimal recursive-descent parser for Ny v0 producing JSON IR v0 (MapBox)
|
||||
|
||||
using "./apps/selfhost/ny-parser-nyash/tokenizer.nyash" as Tokenizer
|
||||
using "./apps/selfhost/ny-parser-nyash/tokenizer.nyash"
|
||||
|
||||
static box ParserV0 {
|
||||
init { tokens, pos }
|
||||
|
||||
parse_program(input) {
|
||||
me.tokens = Tokenizer.tokenize(input)
|
||||
// Error passthrough
|
||||
if me.tokens.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 {
|
||||
return me.tokens
|
||||
}
|
||||
me.pos = 0
|
||||
local stmt = me.parse_stmt()
|
||||
if stmt.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return stmt }
|
||||
local body = new ArrayBox(); body.push(stmt)
|
||||
local prog = new MapBox(); prog.set("version", 0); prog.set("kind", "Program"); prog.set("body", body)
|
||||
return prog
|
||||
}
|
||||
|
||||
parse_stmt() {
|
||||
local tok = me.peek()
|
||||
if tok.get("type") == "RETURN" {
|
||||
me.next()
|
||||
local expr = me.parse_expr()
|
||||
if expr.as_any().toString().indexOf("\"kind\":\"Error\"") >= 0 { return expr }
|
||||
local ret = new MapBox(); ret.set("type", "Return"); ret.set("expr", expr)
|
||||
return ret
|
||||
}
|
||||
return me.err("Expected 'return'")
|
||||
}
|
||||
|
||||
parse_expr() {
|
||||
local left = me.parse_term()
|
||||
loop(true) {
|
||||
local t = me.peek(); local ty = t.get("type")
|
||||
if ty == "+" || ty == "-" {
|
||||
me.next(); local right = me.parse_term()
|
||||
left = me.bin(ty, left, right)
|
||||
} else { break }
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
parse_term() {
|
||||
local left = me.parse_factor()
|
||||
loop(true) {
|
||||
local t = me.peek(); local ty = t.get("type")
|
||||
if ty == "*" || ty == "/" {
|
||||
me.next(); local right = me.parse_factor()
|
||||
left = me.bin(ty, left, right)
|
||||
} else { break }
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
parse_factor() {
|
||||
local t = me.peek(); local ty = t.get("type")
|
||||
if ty == "INT" {
|
||||
me.next();
|
||||
local node = new MapBox(); node.set("type", "Int"); node.set("value", t.get("value"))
|
||||
return node
|
||||
}
|
||||
if ty == "(" {
|
||||
me.next();
|
||||
local e = me.parse_expr()
|
||||
local r = me.peek(); if r.get("type") != ")" { return me.err(") expected") } else { me.next() }
|
||||
return e
|
||||
}
|
||||
return me.err("factor expected")
|
||||
}
|
||||
|
||||
// helpers
|
||||
peek() { return me.tokens.get(me.pos) }
|
||||
next() { me.pos = me.pos + 1; return me.tokens.get(me.pos-1) }
|
||||
bin(op, lhs, rhs) {
|
||||
local m = new MapBox(); m.set("type", "Binary"); m.set("op", op); m.set("lhs", lhs); m.set("rhs", rhs); return m
|
||||
}
|
||||
err(msg) {
|
||||
local err = new MapBox(); err.set("version", 0); err.set("kind", "Error")
|
||||
local e = new MapBox(); e.set("message", msg)
|
||||
local sp = new MapBox(); sp.set("start", me.pos); sp.set("end", me.pos)
|
||||
e.set("span", sp); err.set("error", e)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
// Minimal tokenizer for Ny v0 (ints, + - * /, ( ), return)
|
||||
|
||||
static box Tokenizer {
|
||||
tokenize(input) {
|
||||
local tokens = new ArrayBox()
|
||||
local i = 0
|
||||
local n = input.length()
|
||||
// helper: skip whitespace
|
||||
fn skip_ws() {
|
||||
loop(i < n) {
|
||||
local ch = input.substring(i, i+1)
|
||||
if ch == " " || ch == "\t" || ch == "\r" || ch == "\n" { i = i + 1 } else { return }
|
||||
}
|
||||
}
|
||||
// main loop
|
||||
loop(i < n) {
|
||||
skip_ws()
|
||||
if i >= n { break }
|
||||
local ch = input.substring(i, i+1)
|
||||
if ch == "+" || ch == "-" || ch == "*" || ch == "/" || ch == "(" || ch == ")" {
|
||||
local tok = new MapBox(); tok.set("type", ch)
|
||||
tokens.push(tok); i = i + 1; continue
|
||||
}
|
||||
// keyword: return
|
||||
if i + 6 <= n {
|
||||
local kw = input.substring(i, i+6)
|
||||
if kw == "return" {
|
||||
local t = new MapBox(); t.set("type", "RETURN")
|
||||
tokens.push(t); i = i + 6; continue
|
||||
}
|
||||
}
|
||||
// integer literal
|
||||
if ch >= "0" && ch <= "9" {
|
||||
local j = i
|
||||
loop(j < n) {
|
||||
local cj = input.substring(j, j+1)
|
||||
if cj >= "0" && cj <= "9" { j = j + 1 } else { break }
|
||||
}
|
||||
local num_str = input.substring(i, j)
|
||||
local tnum = new MapBox(); tnum.set("type", "INT"); tnum.set("value", num_str)
|
||||
tokens.push(tnum); i = j; continue
|
||||
}
|
||||
// unknown
|
||||
local err = new MapBox(); err.set("version", 0); err.set("kind", "Error")
|
||||
local e = new MapBox(); e.set("message", "Unknown token");
|
||||
local sp = new MapBox(); sp.set("start", i); sp.set("end", i+1)
|
||||
e.set("span", sp); err.set("error", e)
|
||||
return err
|
||||
}
|
||||
// EOF
|
||||
local eof = new MapBox(); eof.set("type", "EOF"); tokens.push(eof)
|
||||
return tokens
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,217 +0,0 @@
|
||||
// ny_parser_v0 — Stage 1 MVP: Ny -> JSON v0 (minimal)
|
||||
// Supports: return <expr>; expr ::= term (('+'|'-') term)*
|
||||
// term ::= factor (('*'|'/') factor)*
|
||||
// factor::= INT | STRING | '(' expr ')'
|
||||
|
||||
static box Main {
|
||||
// --- Utilities ---
|
||||
is_digit(ch) { return ch >= "0" && ch <= "9" }
|
||||
is_space(ch) {
|
||||
return ch == " " || ch == "\t" || ch == "\n" || ch == "\r"
|
||||
}
|
||||
|
||||
esc_json(s) {
|
||||
// escape backslash and quote for JSON strings
|
||||
local out = ""
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "\\" { out = out + "\\\\" } else {
|
||||
if ch == "\"" { out = out + "\\\"" } else { out = out + ch }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Cursor helpers over source string
|
||||
skip_ws(src, i) {
|
||||
local n = src.length()
|
||||
loop(i < n && me.is_space(src.substring(i, i+1))) { i = i + 1 }
|
||||
return i
|
||||
}
|
||||
|
||||
// (helper match removed; inline checks in parse_program)
|
||||
|
||||
parse_number(src, i) {
|
||||
// returns (json, next_i)
|
||||
local n = src.length()
|
||||
local j = i
|
||||
loop(j < n && me.is_digit(src.substring(j, j+1))) { j = j + 1 }
|
||||
local s = src.substring(i, j)
|
||||
local json = "{\"type\":\"Int\",\"value\":" + s + "}"
|
||||
return json + "@" + j // pack result with '@' separator
|
||||
}
|
||||
|
||||
parse_string(src, i) {
|
||||
local n = src.length()
|
||||
local j = i + 1 // skip opening quote
|
||||
local out = ""
|
||||
loop(j < n) {
|
||||
local ch = src.substring(j, j+1)
|
||||
if ch == "\"" {
|
||||
j = j + 1
|
||||
local json0 = "{\"type\":\"Str\",\"value\":\"" + me.esc_json(out) + "\"}"
|
||||
return json0 + "@" + j
|
||||
}
|
||||
if ch == "\\" && j + 1 < n {
|
||||
local nx = src.substring(j+1, j+2)
|
||||
// minimal escapes (\" and \\)
|
||||
if nx == "\"" { out = out + "\"" } else { if nx == "\\" { out = out + "\\" } else { out = out + nx } }
|
||||
j = j + 2
|
||||
} else {
|
||||
out = out + ch
|
||||
j = j + 1
|
||||
}
|
||||
}
|
||||
// Unterminated string (fallback)
|
||||
local json = "{\"type\":\"Str\",\"value\":\"" + me.esc_json(out) + "\"}"
|
||||
return json + "@" + j
|
||||
}
|
||||
|
||||
// Recursive descent
|
||||
parse_factor(src, i) {
|
||||
// skip ws
|
||||
local nsrc = src.length()
|
||||
loop(i < nsrc && me.is_space(src.substring(i, i+1))) { i = i + 1 }
|
||||
local ch = src.substring(i, i+1)
|
||||
if ch == "(" {
|
||||
// (expr)
|
||||
local p = me.parse_expr(src, i + 1)
|
||||
local at = p.lastIndexOf("@")
|
||||
local ej = p.substring(0, at)
|
||||
local j = me.to_int(p.substring(at+1, p.length()))
|
||||
// skip ws
|
||||
local n2 = src.length()
|
||||
loop(j < n2 && me.is_space(src.substring(j, j+1))) { j = j + 1 }
|
||||
if src.substring(j, j+1) == ")" { j = j + 1 }
|
||||
return ej + "@" + j
|
||||
}
|
||||
if ch == "\"" { return me.parse_string(src, i) }
|
||||
// number
|
||||
return me.parse_number(src, i)
|
||||
}
|
||||
|
||||
parse_term(src, i) {
|
||||
local p = me.parse_factor(src, i)
|
||||
local at = p.lastIndexOf("@")
|
||||
local lhs = p.substring(0, at)
|
||||
local j = me.to_int(p.substring(at+1, p.length()))
|
||||
local cont = 1
|
||||
loop(cont == 1) {
|
||||
// skip ws
|
||||
local n3 = src.length()
|
||||
loop(j < n3 && me.is_space(src.substring(j, j+1))) { j = j + 1 }
|
||||
if j >= src.length() { cont = 0 } else {
|
||||
local op = src.substring(j, j+1)
|
||||
if op != "*" && op != "/" { cont = 0 } else {
|
||||
// parse rhs
|
||||
local q = me.parse_factor(src, j+1)
|
||||
local at2 = q.lastIndexOf("@")
|
||||
local rhs = q.substring(0, at2)
|
||||
j = me.to_int(q.substring(at2+1, q.length()))
|
||||
lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
|
||||
}
|
||||
}
|
||||
}
|
||||
return lhs + "@" + j
|
||||
}
|
||||
|
||||
parse_expr(src, i) {
|
||||
local p = me.parse_term(src, i)
|
||||
local at = p.lastIndexOf("@")
|
||||
local lhs = p.substring(0, at)
|
||||
local j = me.to_int(p.substring(at+1, p.length()))
|
||||
local cont = 1
|
||||
loop(cont == 1) {
|
||||
// skip ws
|
||||
local n4 = src.length()
|
||||
loop(j < n4 && me.is_space(src.substring(j, j+1))) { j = j + 1 }
|
||||
if j >= src.length() { cont = 0 } else {
|
||||
local op = src.substring(j, j+1)
|
||||
if op != "+" && op != "-" { cont = 0 } else {
|
||||
// parse rhs
|
||||
local q = me.parse_term(src, j+1)
|
||||
local at2 = q.lastIndexOf("@")
|
||||
local rhs = q.substring(0, at2)
|
||||
j = me.to_int(q.substring(at2+1, q.length()))
|
||||
lhs = "{\"type\":\"Binary\",\"op\":\"" + op + "\",\"lhs\":" + lhs + ",\"rhs\":" + rhs + "}"
|
||||
}
|
||||
}
|
||||
}
|
||||
return lhs + "@" + j
|
||||
}
|
||||
|
||||
to_int(s) {
|
||||
// parse decimal int from string s
|
||||
// 簡易に桁を読む(ここでは利用側が整数のみで使う)
|
||||
// ただしここは内部専用で index を取り出すだけなので、s は数字のみ想定
|
||||
// 実装簡略化のため、長さ0なら0、それ以外は手動で畳み込み
|
||||
local n = s.length()
|
||||
if n == 0 { return 0 }
|
||||
local i = 0
|
||||
local acc = 0
|
||||
loop(i < n) {
|
||||
local d = s.substring(i, i+1)
|
||||
local dv = 0
|
||||
if d == "1" { dv = 1 } else {
|
||||
if d == "2" { dv = 2 } else {
|
||||
if d == "3" { dv = 3 } else {
|
||||
if d == "4" { dv = 4 } else {
|
||||
if d == "5" { dv = 5 } else {
|
||||
if d == "6" { dv = 6 } else {
|
||||
if d == "7" { dv = 7 } else {
|
||||
if d == "8" { dv = 8 } else {
|
||||
if d == "9" { dv = 9 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
acc = acc * 10 + dv
|
||||
i = i + 1
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
parse_program(src) {
|
||||
// optional leading ws + optional 'return'
|
||||
// skip leading ws
|
||||
local i = 0
|
||||
local n0 = src.length()
|
||||
loop(i < n0 && me.is_space(src.substring(i, i+1))) { i = i + 1 }
|
||||
local j = i
|
||||
local n = src.length()
|
||||
if i + 6 <= n && src.substring(i, i+6) == "return" { j = i + 6 }
|
||||
local p = me.parse_expr(src, j)
|
||||
local at = p.lastIndexOf("@")
|
||||
local ej = p.substring(0, at)
|
||||
local body = "[{\"type\":\"Return\",\"expr\":" + ej + "}]"
|
||||
return "{\"version\":0,\"kind\":\"Program\",\"body\":" + body + "}"
|
||||
}
|
||||
|
||||
read_all(path) {
|
||||
local fb = new FileBox()
|
||||
fb.open(path, "r")
|
||||
local s = fb.read()
|
||||
fb.close()
|
||||
return s
|
||||
}
|
||||
|
||||
main(args) {
|
||||
// usage: nyash --backend vm apps/selfhost/parser/ny_parser_v0/main.nyash
|
||||
// Input source is read from tmp/ny_parser_input.ny (written by wrapper script)
|
||||
local console = new ConsoleBox()
|
||||
local src = null
|
||||
local default_path = "tmp/ny_parser_input.ny"
|
||||
src = me.read_all(default_path)
|
||||
if src == null { src = "return 1+2*3" }
|
||||
local json = me.parse_program(src)
|
||||
console.println(json)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
// child file
|
||||
box Child {
|
||||
hello() {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
// cycle A -> B -> A
|
||||
using "dep_smoke_cycle_b.nyash"
|
||||
|
||||
box A {
|
||||
id() { return 1 }
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
// cycle B -> A
|
||||
using "dep_smoke_cycle_a.nyash"
|
||||
|
||||
box B {
|
||||
id() { return 2 }
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
// root smoke for include-only tree
|
||||
using "dep_smoke_child.nyash"
|
||||
|
||||
box Root {
|
||||
main() {
|
||||
// a string containing include "should_not_match.nyash"
|
||||
local s = "text include \"dummy.nyash\" text"
|
||||
// a comment with include "ignored.nyash"
|
||||
// include "ignored.nyash"
|
||||
# include "ignored2.nyash"
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,253 +0,0 @@
|
||||
// dep_tree.nyash — Build dependency info for a Nyash script (include + using/module)
|
||||
|
||||
static box DepTree {
|
||||
init { visited }
|
||||
|
||||
// Public API: build tree for entry path
|
||||
build(entry_path) {
|
||||
me.visited = new MapBox()
|
||||
return me.node(entry_path)
|
||||
}
|
||||
|
||||
// Read entire file as string via FileBox
|
||||
read_file(path) {
|
||||
local fb = new FileBox()
|
||||
local ok = fb.open(path, "r")
|
||||
if ok == false { return null }
|
||||
local content = fb.read()
|
||||
fb.close()
|
||||
return content
|
||||
}
|
||||
|
||||
// Extract include paths from source: include "./path.nyash"
|
||||
extract_includes(src) {
|
||||
local out = new ArrayBox()
|
||||
if src == null { return out }
|
||||
local i = 0
|
||||
local n = src.length()
|
||||
loop(i < n) {
|
||||
local j = src.indexOf("include \"", i)
|
||||
if j < 0 { break }
|
||||
local k = j + 9 // after include "
|
||||
local q = src.indexOf("\"", k)
|
||||
if q < 0 { break }
|
||||
out.push(src.substring(k, q))
|
||||
i = q + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Extract using directives (script lines):
|
||||
// using ns
|
||||
// using ns as Alias
|
||||
// using "path" as Name
|
||||
// and comment form: // @using ns[ as Alias]
|
||||
extract_usings(src) {
|
||||
local out = new ArrayBox()
|
||||
if src == null { return out }
|
||||
local lines = me.split_lines(src)
|
||||
local i = 0
|
||||
loop(i < lines.length()) {
|
||||
local line = lines.get(i).trim()
|
||||
local t = line
|
||||
if t.startsWith("// @using ") { t = t.substring(10, t.length()).trim() }
|
||||
else if t.startsWith("using ") { t = t.substring(6, t.length()).trim() }
|
||||
else { i = i + 1; continue }
|
||||
|
||||
// optional trailing semicolon
|
||||
if t.endsWith(";") { t = t.substring(0, t.length()-1).trim() }
|
||||
|
||||
local rec = new MapBox()
|
||||
// Split alias
|
||||
local as_pos = t.indexOf(" as ")
|
||||
local target = t
|
||||
local alias = null
|
||||
if as_pos >= 0 {
|
||||
target = t.substring(0, as_pos).trim()
|
||||
alias = t.substring(as_pos+4, t.length()).trim()
|
||||
}
|
||||
rec.set("target", target)
|
||||
if alias != null { rec.set("alias", alias) }
|
||||
// classify
|
||||
if target.startsWith("\"") || target.startsWith("./") || target.startsWith("/") || target.endsWith(".nyash") {
|
||||
rec.set("kind", "path")
|
||||
// strip quotes
|
||||
if target.startsWith("\"") { rec.set("target", target.substring(1, target.length()-1)) }
|
||||
} else {
|
||||
rec.set("kind", "ns")
|
||||
}
|
||||
out.push(rec)
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Extract modules mapping from // @module ns=path
|
||||
extract_modules(src) {
|
||||
local out = new ArrayBox()
|
||||
if src != null {
|
||||
local lines = me.split_lines(src)
|
||||
local i = 0
|
||||
loop(i < lines.length()) {
|
||||
local line = lines.get(i).trim()
|
||||
if line.startsWith("// @module ") {
|
||||
local rest = line.substring(11, line.length()).trim()
|
||||
local eq = rest.indexOf("=")
|
||||
if eq > 0 {
|
||||
local ns = rest.substring(0, eq).trim()
|
||||
local path = rest.substring(eq+1, rest.length()).trim()
|
||||
path = me.strip_quotes(path)
|
||||
local m = new MapBox(); m.set("ns", ns); m.set("path", path); out.push(m)
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Build a node: { path, includes:[...], using:[...], modules:[...], children:[...] }
|
||||
node(path) {
|
||||
if me.visited.has(path) { return me.leaf(path) }
|
||||
me.visited.set(path, true)
|
||||
|
||||
local m = new MapBox(); m.set("path", path)
|
||||
local src = me.read_file(path)
|
||||
if src == null { m.set("error", "read_fail"); me.ensure_arrays(m); return m }
|
||||
|
||||
local base_dir = me.dirname(path)
|
||||
|
||||
// includes
|
||||
local incs = me.extract_includes(src)
|
||||
m.set("includes", incs)
|
||||
// usings
|
||||
local us = me.extract_usings(src)
|
||||
m.set("using", us)
|
||||
// modules mapping (script-level)
|
||||
local mods = me.extract_modules(src)
|
||||
m.set("modules", mods)
|
||||
|
||||
// children = includes + resolved using(path) + resolved using(ns via search paths)
|
||||
local children = new ArrayBox()
|
||||
// include children
|
||||
local i = 0
|
||||
loop(i < incs.length()) {
|
||||
local p = incs.get(i)
|
||||
local child_path = me.resolve_path(base_dir, p)
|
||||
children.push(me.node(child_path))
|
||||
i = i + 1
|
||||
}
|
||||
// using(path) children
|
||||
i = 0
|
||||
loop(i < us.length()) {
|
||||
local u = us.get(i)
|
||||
if u.get("kind") == "path" {
|
||||
local p = u.get("target")
|
||||
local child_path = me.resolve_path(base_dir, p)
|
||||
children.push(me.node(child_path))
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
// using(ns) children resolved via search paths
|
||||
local search = me.default_using_paths()
|
||||
i = 0
|
||||
loop(i < us.length()) {
|
||||
local u = us.get(i)
|
||||
if u.get("kind") == "ns" {
|
||||
local ns = u.get("target")
|
||||
local rel = ns.replace(".", "/") + ".nyash"
|
||||
local found = me.search_in_paths(base_dir, search, rel)
|
||||
if found != null { children.push(me.node(found)) }
|
||||
else {
|
||||
// annotate unresolved
|
||||
u.set("unresolved", true)
|
||||
u.set("hint", rel)
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
m.set("children", children)
|
||||
return m
|
||||
}
|
||||
|
||||
// Helpers
|
||||
ensure_arrays(m) { m.set("includes", new ArrayBox()); m.set("using", new ArrayBox()); m.set("modules", new ArrayBox()); m.set("children", new ArrayBox()) }
|
||||
|
||||
default_using_paths() {
|
||||
// Best-effort defaults prioritized for selfhost
|
||||
local arr = new ArrayBox()
|
||||
arr.push("apps/selfhost"); arr.push("apps"); arr.push("lib"); arr.push(".")
|
||||
return arr
|
||||
}
|
||||
|
||||
split_lines(src) {
|
||||
local out = new ArrayBox()
|
||||
local i = 0; local n = src.length(); local start = 0
|
||||
loop(i <= n) {
|
||||
if i == n || src.substring(i, i+1) == "\n" {
|
||||
out.push(src.substring(start, i))
|
||||
start = i + 1
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
strip_quotes(s) {
|
||||
if s == null { return null }
|
||||
if s.length() >= 2 && s.substring(0,1) == "\"" && s.substring(s.length()-1, s.length()) == "\"" {
|
||||
return s.substring(1, s.length()-1)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
dirname(path) {
|
||||
local pb = new PathBox();
|
||||
local d = pb.dirname(path)
|
||||
if d != null { return d }
|
||||
local i = path.lastIndexOf("/")
|
||||
if i < 0 { return "." }
|
||||
return path.substring(0, i)
|
||||
}
|
||||
|
||||
resolve_path(base, rel) {
|
||||
if rel.indexOf("/") == 0 { return rel }
|
||||
if rel.startsWith("./") || rel.startsWith("../") {
|
||||
local pb = new PathBox();
|
||||
local j = pb.join(base, rel)
|
||||
if j != null { return j }
|
||||
return base + "/" + rel
|
||||
}
|
||||
return rel
|
||||
}
|
||||
|
||||
search_in_paths(base, paths, rel) {
|
||||
// try relative to file first
|
||||
local pb = new PathBox();
|
||||
local j = pb.join(base, rel)
|
||||
if me.file_exists(j) { return j }
|
||||
// then search list
|
||||
local i = 0
|
||||
loop(i < paths.length()) {
|
||||
local p = paths.get(i)
|
||||
local cand = pb.join(p, rel)
|
||||
if me.file_exists(cand) { return cand }
|
||||
i = i + 1
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
file_exists(path) {
|
||||
// Use FileBox.open in read mode as exists check
|
||||
local fb = new FileBox();
|
||||
local ok = fb.open(path, "r")
|
||||
if ok == false { return false }
|
||||
fb.close(); return true
|
||||
}
|
||||
|
||||
leaf(path) {
|
||||
local m = new MapBox(); m.set("path", path); m.set("children", new ArrayBox()); m.set("note", "visited")
|
||||
return m
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
// dep_tree_main.nyash — entry script to print JSON tree
|
||||
|
||||
using "./apps/selfhost/tools/dep_tree.nyash" as DepTree
|
||||
using "./apps/selfhost/tools/dep_tree.nyash"
|
||||
|
||||
static box Main {
|
||||
main(args) {
|
||||
local console = new ConsoleBox()
|
||||
local path = null
|
||||
if args != null && args.length() >= 1 { path = args.get(0) }
|
||||
if path == null || path == "" {
|
||||
// default sample
|
||||
path = "apps/selfhost/ny-parser-nyash/main.nyash"
|
||||
}
|
||||
local tree = DepTree.build(path)
|
||||
console.println(tree.toJson())
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
// dep_tree_min_string.nyash — minimal include-only dependency tree (no Array/Map plugins)
|
||||
|
||||
static box Main {
|
||||
has_in_stack(stack, p) {
|
||||
// check if stack contains "\n" + p + "\n"
|
||||
local t = "\n" + p + "\n"
|
||||
local n = stack.length()
|
||||
local m = t.length()
|
||||
if m == 0 { return 0 }
|
||||
local i = 0
|
||||
loop(i + m <= n) {
|
||||
if stack.substring(i, i+m) == t { return 1 }
|
||||
i = i + 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
read_all(path) {
|
||||
local fb = new FileBox()
|
||||
local ok = fb.open(path, "r")
|
||||
if ok == false { return null }
|
||||
local s = fb.read()
|
||||
fb.close()
|
||||
return s
|
||||
}
|
||||
|
||||
dirname(path) {
|
||||
local pb = new PathBox()
|
||||
local d = pb.dirname(path)
|
||||
if d != null { return d }
|
||||
local i = path.lastIndexOf("/")
|
||||
if i < 0 { return "." }
|
||||
return path.substring(0, i)
|
||||
}
|
||||
|
||||
join(base, rel) {
|
||||
local pb = new PathBox()
|
||||
local j = pb.join(base, rel)
|
||||
if j != null { return j }
|
||||
return base + "/" + rel
|
||||
}
|
||||
|
||||
esc_json(s) {
|
||||
// very small escaper: replace \ and "
|
||||
local out = ""
|
||||
local i = 0
|
||||
local n = s.length()
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "\\" { out = out + "\\\\" } else {
|
||||
if ch == "\"" { out = out + "\\\"" } else { out = out + ch }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
node_json(path, stack, depth) {
|
||||
// safety valve: max depth
|
||||
if depth >= 64 {
|
||||
return "{\\\"path\\\":\\\"" + me.esc_json(path) + "\\\",\\\"includes\\\":[],\\\"children\\\":[]}"
|
||||
}
|
||||
local src = me.read_all(path)
|
||||
if src == null {
|
||||
return "{\\\"path\\\":\\\"" + me.esc_json(path) + "\\\",\\\"includes\\\":[],\\\"children\\\":[]}"
|
||||
}
|
||||
local base = me.dirname(path)
|
||||
local incs = ""
|
||||
local inc_first = 1
|
||||
local children = ""
|
||||
local child_first = 1
|
||||
local i = 0
|
||||
local n = src.length()
|
||||
local in_str = 0
|
||||
local in_cmt = 0
|
||||
loop(i < n) {
|
||||
local ch = src.substring(i, i+1)
|
||||
// handle line comments (// or #)
|
||||
if in_cmt == 1 {
|
||||
if ch == "\n" { in_cmt = 0 }
|
||||
i = i + 1
|
||||
} else if in_str == 1 {
|
||||
if ch == "\"" {
|
||||
// if previous is not backslash, close
|
||||
if i == 0 { in_str = 0 } else {
|
||||
local prev = src.substring(i-1, i)
|
||||
if prev != "\\" { in_str = 0 }
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
} else if ch == "/" && i + 1 < n && src.substring(i+1, i+2) == "/" {
|
||||
// start // comment
|
||||
in_cmt = 1
|
||||
i = i + 2
|
||||
} else if ch == "#" {
|
||||
// start # comment
|
||||
in_cmt = 1
|
||||
i = i + 1
|
||||
} else if ch == "\"" {
|
||||
// enter string
|
||||
in_str = 1
|
||||
i = i + 1
|
||||
} else if i + 9 <= n && src.substring(i, i+9) == "include \"" {
|
||||
// look for include "..."
|
||||
local j = i + 9
|
||||
// find closing quote (respect escapes) without using break
|
||||
local found = 0
|
||||
loop(j < n && found == 0) {
|
||||
if src.substring(j, j+1) == "\"" {
|
||||
local prev = src.substring(j-1, j)
|
||||
if prev != "\\" { found = 1 } else { j = j + 1 }
|
||||
} else {
|
||||
j = j + 1
|
||||
}
|
||||
}
|
||||
if found == 1 {
|
||||
local p = src.substring(i+9, j)
|
||||
if inc_first == 1 {
|
||||
incs = incs + "\"" + me.esc_json(p) + "\""
|
||||
inc_first = 0
|
||||
} else {
|
||||
incs = incs + ",\"" + me.esc_json(p) + "\""
|
||||
}
|
||||
local child_path = me.join(base, p)
|
||||
// cycle detection: if child_path already in stack, do not recurse
|
||||
local cj = null
|
||||
if me.has_in_stack(stack, child_path) == 1 {
|
||||
cj = "{\\\"path\\\":\\\"" + me.esc_json(child_path) + "\\\",\\\"includes\\\":[],\\\"children\\\":[]}"
|
||||
} else {
|
||||
cj = me.node_json(child_path, stack + child_path + "\n", depth + 1)
|
||||
}
|
||||
if child_first == 1 {
|
||||
children = children + cj
|
||||
child_first = 0
|
||||
} else {
|
||||
children = children + "," + cj
|
||||
}
|
||||
i = j + 1
|
||||
} else {
|
||||
i = i + 1
|
||||
}
|
||||
} else {
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return "{\\\"path\\\":\\\"" + me.esc_json(path) + "\\\",\\\"includes\\\":[" + incs + "],\\\"children\\\":[" + children + "]}"
|
||||
}
|
||||
|
||||
main(args) {
|
||||
local console = new ConsoleBox()
|
||||
// Determine entry path (avoid stdin to keep VM path simple)
|
||||
local entry = null
|
||||
if args != null && args.length() >= 1 { entry = args.get(0) }
|
||||
if entry == null || entry == "" { entry = "apps/selfhost/ny-parser-nyash/main.nyash" }
|
||||
local tree = me.node_json(entry, "\n" + entry + "\n", 0)
|
||||
local out = "{\\\"version\\\":1,\\\"root_path\\\":\\\"" + me.esc_json(entry) + "\\\",\\\"tree\\\":" + tree + "}"
|
||||
console.println(out)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,265 +0,0 @@
|
||||
// dep_tree_simple.nyash — dependency tree (include + using/module) in a single static box
|
||||
|
||||
static box Main {
|
||||
// ---- file utils ----
|
||||
read_all(path) {
|
||||
local fb = new FileBox()
|
||||
local ok = fb.open(path, "r")
|
||||
if ok == false { return null }
|
||||
local s = fb.read()
|
||||
fb.close()
|
||||
return s
|
||||
}
|
||||
|
||||
file_exists(path) {
|
||||
local fb = new FileBox()
|
||||
local ok = fb.open(path, "r")
|
||||
if ok == false { return false }
|
||||
fb.close()
|
||||
return true
|
||||
}
|
||||
|
||||
dirname(path) {
|
||||
local pb = new PathBox()
|
||||
local d = pb.dirname(path)
|
||||
if d != null { return d }
|
||||
local i = path.lastIndexOf("/")
|
||||
if i < 0 { return "." }
|
||||
return path.substring(0, i)
|
||||
}
|
||||
|
||||
join(base, rel) {
|
||||
local pb = new PathBox()
|
||||
local j = pb.join(base, rel)
|
||||
if j != null { return j }
|
||||
return base + "/" + rel
|
||||
}
|
||||
|
||||
// ---- text utils ----
|
||||
split_lines(src) {
|
||||
// return { arr, len }
|
||||
local pair = new MapBox()
|
||||
local out = new ArrayBox()
|
||||
local len = 0
|
||||
local i = 0
|
||||
local n = src.length()
|
||||
local start = 0
|
||||
loop(true) {
|
||||
if i == n { out.push(src.substring(start, i)) len = len + 1 pair.set("arr", out) pair.set("len", len) return pair }
|
||||
local ch = src.substring(i, i+1)
|
||||
if ch == "\n" { out.push(src.substring(start, i)) len = len + 1 start = i + 1 }
|
||||
i = i + 1
|
||||
}
|
||||
pair.set("arr", out)
|
||||
pair.set("len", len)
|
||||
return pair
|
||||
}
|
||||
|
||||
// ---- scanners ----
|
||||
scan_includes(src) {
|
||||
local pair = new MapBox()
|
||||
local out = new ArrayBox()
|
||||
local out_len = 0
|
||||
if src == null { pair.set("arr", out) pair.set("len", out_len) return pair }
|
||||
local lp = me.split_lines(src)
|
||||
local lines = lp.get("arr")
|
||||
local lines_len = lp.get("len")
|
||||
local i = 0
|
||||
loop(i < lines_len) {
|
||||
local t = lines.get(i).trim()
|
||||
if t.startsWith("include \"") {
|
||||
local rest = t.substring(9, t.length())
|
||||
local j = 0
|
||||
local q = -1
|
||||
loop(j < rest.length()) {
|
||||
if rest.substring(j, j+1) == "\"" { q = j j = rest.length() }
|
||||
j = j + 1
|
||||
}
|
||||
if q >= 0 { out.push(rest.substring(0, q)) out_len = out_len + 1 }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
pair.set("arr", out)
|
||||
pair.set("len", out_len)
|
||||
return pair
|
||||
}
|
||||
|
||||
scan_usings(src) {
|
||||
// return { arr, len }
|
||||
local pair = new MapBox()
|
||||
local out = new ArrayBox()
|
||||
local out_len = 0
|
||||
if src == null { pair.set("arr", out) pair.set("len", out_len) return pair }
|
||||
local lp = me.split_lines(src)
|
||||
local lines = lp.get("arr")
|
||||
local lines_len = lp.get("len")
|
||||
local i = 0
|
||||
loop(i < lines_len) {
|
||||
local t0 = lines.get(i).trim()
|
||||
local matched = false
|
||||
local t = t0
|
||||
if t0.startsWith("// @using ") { t = t0.substring(10, t0.length()) matched = true } else {
|
||||
if t0.startsWith("using ") { t = t0.substring(6, t0.length()) matched = true }
|
||||
}
|
||||
if matched {
|
||||
local as_pos = t.indexOf(" as ")
|
||||
local target = t
|
||||
local alias = null
|
||||
if as_pos >= 0 { target = t.substring(0, as_pos).trim() alias = t.substring(as_pos+4, t.length()).trim() }
|
||||
local rec = new MapBox()
|
||||
rec.set("target", target)
|
||||
if alias != null { rec.set("alias", alias) }
|
||||
if target.startsWith("./") || target.startsWith("/") || target.endsWith(".nyash") { rec.set("kind", "path") } else { rec.set("kind", "namespace") }
|
||||
out.push(rec)
|
||||
out_len = out_len + 1
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
pair.set("arr", out)
|
||||
pair.set("len", out_len)
|
||||
return pair
|
||||
}
|
||||
|
||||
scan_modules(src) {
|
||||
// return { arr, len }
|
||||
local pair = new MapBox()
|
||||
local out = new ArrayBox()
|
||||
local out_len = 0
|
||||
if src == null { pair.set("arr", out) pair.set("len", out_len) return pair }
|
||||
local lp = me.split_lines(src)
|
||||
local lines = lp.get("arr")
|
||||
local lines_len = lp.get("len")
|
||||
local i = 0
|
||||
loop(i < lines_len) {
|
||||
local t = lines.get(i).trim()
|
||||
if t.startsWith("// @module ") {
|
||||
local rest = t.substring(11, t.length())
|
||||
local eq = rest.indexOf("=")
|
||||
if eq > 0 {
|
||||
local ns = rest.substring(0, eq).trim()
|
||||
local path = rest.substring(eq+1, t.length()).trim()
|
||||
local m = new MapBox()
|
||||
m.set("namespace", ns)
|
||||
m.set("path", path)
|
||||
out.push(m)
|
||||
out_len = out_len + 1
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
pair.set("arr", out)
|
||||
pair.set("len", out_len)
|
||||
return pair
|
||||
}
|
||||
|
||||
default_using_paths() {
|
||||
local a = new ArrayBox()
|
||||
a.push("apps/selfhost")
|
||||
a.push("apps")
|
||||
a.push("lib")
|
||||
a.push(".")
|
||||
return a
|
||||
}
|
||||
|
||||
resolve_ns(base_dir, ns) {
|
||||
local rel = ns.replace(".", "/") + ".nyash"
|
||||
local cand0 = me.join(base_dir, rel)
|
||||
if me.file_exists(cand0) { return cand0 }
|
||||
local paths = me.default_using_paths()
|
||||
local paths_len = 4
|
||||
local i = 0
|
||||
loop(i < paths_len) {
|
||||
local cand = me.join(paths.get(i), rel)
|
||||
if me.file_exists(cand) { return cand }
|
||||
i = i + 1
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
node_for(path, visited) {
|
||||
if visited.has(path) {
|
||||
local m = new MapBox()
|
||||
m.set("path", path)
|
||||
m.set("children", new ArrayBox())
|
||||
m.set("includes", new ArrayBox())
|
||||
m.set("uses", new ArrayBox())
|
||||
m.set("modules", new ArrayBox())
|
||||
m.set("note", "visited")
|
||||
return m
|
||||
}
|
||||
visited.set(path, true)
|
||||
local out = new MapBox()
|
||||
out.set("path", path)
|
||||
local src = me.read_all(path)
|
||||
if src == null {
|
||||
out.set("error", "read_fail")
|
||||
out.set("includes", new ArrayBox())
|
||||
out.set("uses", new ArrayBox())
|
||||
out.set("modules", new ArrayBox())
|
||||
out.set("children", new ArrayBox())
|
||||
return out
|
||||
}
|
||||
local base = me.dirname(path)
|
||||
local incp = me.scan_includes(src)
|
||||
local incs = incp.get("arr")
|
||||
local incs_len = incp.get("len")
|
||||
local usp = me.scan_usings(src)
|
||||
local uses = usp.get("arr")
|
||||
local uses_len = usp.get("len")
|
||||
local modp = me.scan_modules(src)
|
||||
local mods = modp.get("arr")
|
||||
local mods_len = modp.get("len")
|
||||
out.set("includes", incs)
|
||||
out.set("uses", uses)
|
||||
out.set("modules", mods)
|
||||
local mod_map = new MapBox()
|
||||
local mi = 0
|
||||
loop(mi < mods_len) {
|
||||
local mm = mods.get(mi)
|
||||
mod_map.set(mm.get("namespace"), mm.get("path"))
|
||||
mi = mi + 1
|
||||
}
|
||||
local children = new ArrayBox()
|
||||
local i = 0
|
||||
loop(i < incs_len) {
|
||||
local child_path = me.join(base, incs.get(i))
|
||||
children.push(me.node_for(child_path, visited))
|
||||
i = i + 1
|
||||
}
|
||||
i = 0
|
||||
loop(i < uses_len) {
|
||||
local u = uses.get(i)
|
||||
if u.get("kind") == "path" {
|
||||
local p = me.join(base, u.get("target"))
|
||||
if me.file_exists(p) { u.set("resolved", p) children.push(me.node_for(p, visited)) } else { u.set("unresolved", true) u.set("hint", p) }
|
||||
} else {
|
||||
local tgt = u.get("target")
|
||||
local via = mod_map.get(tgt)
|
||||
if via != null {
|
||||
if me.file_exists(via) { u.set("resolved", via) children.push(me.node_for(via, visited)) } else { u.set("unresolved", true) u.set("hint", via) }
|
||||
} else {
|
||||
local found = me.resolve_ns(base, tgt)
|
||||
if found != null { u.set("resolved", found) children.push(me.node_for(found, visited)) } else { u.set("unresolved", true) u.set("hint", tgt) }
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
out.set("children", children)
|
||||
return out
|
||||
}
|
||||
|
||||
main(args) {
|
||||
local console = new ConsoleBox()
|
||||
local entry = null
|
||||
if args != null && args.length() >= 1 { entry = args.get(0) }
|
||||
if entry == null || entry == "" { entry = "apps/selfhost/ny-parser-nyash/main.nyash" }
|
||||
local visited = new MapBox()
|
||||
local tree = me.node_for(entry, visited)
|
||||
local root = new MapBox()
|
||||
root.set("version", 1)
|
||||
root.set("root_path", entry)
|
||||
root.set("tree", tree)
|
||||
console.println(root.toJson())
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
Layer Guard — selfhost/vm
|
||||
|
||||
Scope and responsibility
|
||||
- Minimal Ny-based executors and helpers for self‑hosting experiments.
|
||||
- Responsibilities: trial executors (MIR JSON v0), tiny helpers (scan/binop/compare), smoke drivers.
|
||||
- Forbidden: full parser implementation, heavy runtime logic, code generation.
|
||||
|
||||
Imports policy (SSOT)
|
||||
- Dev/CI: file-using allowed; drivers may embed JSON for tiny smokes.
|
||||
- Prod: prefer `nyash.toml` mapping under `[modules.selfhost.*]`.
|
||||
|
||||
Notes
|
||||
- MirVmMin covers: const/binop/compare/ret (M2). Branch/jump/phi are later.
|
||||
- Keep changes minimal and spec‑neutral; new behavior is gated by new tests.
|
||||
@ -1,61 +0,0 @@
|
||||
// Mini-VM JSON cursor helpers (extracted)
|
||||
// One static box per file per using/include policy
|
||||
static box MiniJsonCur {
|
||||
_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 }
|
||||
// Skip whitespace from pos; return first non-ws index or -1
|
||||
next_non_ws(s, pos) {
|
||||
local i = pos
|
||||
local n = s.length()
|
||||
loop (i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch != " " && ch != "\n" && ch != "\r" && ch != "\t" { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
// Read a quoted string starting at pos '"'; returns decoded string (no state)
|
||||
read_quoted_from(s, pos) {
|
||||
local i = pos
|
||||
if s.substring(i, i+1) != "\"" { return "" }
|
||||
i = i + 1
|
||||
local out = ""
|
||||
local n = s.length()
|
||||
loop (i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "\"" { break }
|
||||
if ch == "\\" {
|
||||
i = i + 1
|
||||
ch = s.substring(i, i+1)
|
||||
}
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
// Read consecutive digits from pos
|
||||
read_digits_from(s, pos) {
|
||||
local out = ""
|
||||
local i = pos
|
||||
// guard against invalid position (null/negative)
|
||||
if i == null { return out }
|
||||
if i < 0 { return out }
|
||||
loop (true) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "" { break }
|
||||
// inline digit check to avoid same-box method dispatch
|
||||
if ch == "0" { out = out + ch i = i + 1 continue }
|
||||
if ch == "1" { out = out + ch i = i + 1 continue }
|
||||
if ch == "2" { out = out + ch i = i + 1 continue }
|
||||
if ch == "3" { out = out + ch i = i + 1 continue }
|
||||
if ch == "4" { out = out + ch i = i + 1 continue }
|
||||
if ch == "5" { out = out + ch i = i + 1 continue }
|
||||
if ch == "6" { out = out + ch i = i + 1 continue }
|
||||
if ch == "7" { out = out + ch i = i + 1 continue }
|
||||
if ch == "8" { out = out + ch i = i + 1 continue }
|
||||
if ch == "9" { out = out + ch i = i + 1 continue }
|
||||
break
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,774 +0,0 @@
|
||||
using selfhost.vm.boxes.json_cur as MiniJson
|
||||
using selfhost.common.mini_vm_scan as MiniVmScan
|
||||
using selfhost.common.mini_vm_binop as MiniVmBinOp
|
||||
using selfhost.common.mini_vm_compare as MiniVmCompare
|
||||
using selfhost.vm.boxes.mini_vm_prints as MiniVmPrints
|
||||
|
||||
static box MiniVm {
|
||||
_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
|
||||
}
|
||||
_str_to_int(s) { return new MiniVmScan()._str_to_int(s) }
|
||||
_int_to_str(n) { return new MiniVmScan()._int_to_str(n) }
|
||||
read_digits(json, pos) { return new MiniJson().read_digits_from(json, pos) }
|
||||
// Read a JSON string starting at position pos (at opening quote); returns the decoded string
|
||||
read_json_string(json, pos) { return new MiniJson().read_quoted_from(json, pos) }
|
||||
// helper: find needle from position pos
|
||||
index_of_from(hay, needle, pos) { return new MiniVmScan().index_of_from(hay, needle, pos) }
|
||||
// helper: next non-whitespace character index from pos
|
||||
next_non_ws(json, pos) { return new MiniJson().next_non_ws(json, pos) }
|
||||
// ——— Helpers (as box methods) ———
|
||||
|
||||
// Minimal: Print(BinaryOp) with operator "+"; supports string+string and int+int
|
||||
// try_print_binop_at moved to MiniVmBinOp
|
||||
|
||||
// Greedy fallback: detect BinaryOp int+int by pattern regardless of field order nuances
|
||||
// try_print_binop_int_greedy moved to MiniVmBinOp
|
||||
|
||||
// Fallback: within the current Print's expression BinaryOp object, scan for two numeric values and sum
|
||||
// try_print_binop_sum_any moved to MiniVmBinOp
|
||||
|
||||
// Deterministic: within the first Print.expression BinaryOp('+'),
|
||||
// find exactly two numeric values from successive '"value":' fields and sum.
|
||||
// Stops after collecting two ints; bounded strictly by the expression object.
|
||||
// try_print_binop_sum_expr_values moved to MiniVmBinOp
|
||||
|
||||
// Simpler deterministic fallback: after the first BinaryOp '+',
|
||||
// scan forward for two successive 'value' fields and sum their integer digits.
|
||||
// This avoids brace matching and remains bounded by two finds.
|
||||
// try_print_binop_sum_after_bop moved to MiniVmBinOp
|
||||
|
||||
// Direct typed BinaryOp(int+int) matcher using explicit left/right literal paths
|
||||
try_print_binop_typed_direct(json) {
|
||||
local k_left = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local k_right = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local lp = json.indexOf(k_left)
|
||||
if lp < 0 { return -1 }
|
||||
local ld = read_digits(json, lp + k_left.length())
|
||||
if ld == "" { return -1 }
|
||||
local rp = index_of_from(json, k_right, lp + k_left.length())
|
||||
if rp < 0 { return -1 }
|
||||
local rd = read_digits(json, rp + k_right.length())
|
||||
if rd == "" { return -1 }
|
||||
print(_int_to_str(_str_to_int(ld) + _str_to_int(rd)))
|
||||
return rp + k_right.length()
|
||||
}
|
||||
|
||||
// Tokenized typed extractor: search left/right blocks then type/value pairs
|
||||
try_print_binop_typed_tokens(json) {
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = json.indexOf(k_bo)
|
||||
if bpos < 0 { return -1 }
|
||||
local lp = index_of_from(json, "\"left\":", bpos)
|
||||
if lp < 0 { return -1 }
|
||||
local kt = "\"type\":\"int\""
|
||||
local kv = "\"value\":"
|
||||
local tp1 = index_of_from(json, kt, lp)
|
||||
if tp1 < 0 { return -1 }
|
||||
local vp1 = index_of_from(json, kv, tp1)
|
||||
if vp1 < 0 { return -1 }
|
||||
local ld = read_digits(json, vp1 + kv.length())
|
||||
if ld == "" { return -1 }
|
||||
local rp = index_of_from(json, "\"right\":", lp)
|
||||
if rp < 0 { return -1 }
|
||||
local tp2 = index_of_from(json, kt, rp)
|
||||
if tp2 < 0 { return -1 }
|
||||
local vp2 = index_of_from(json, kv, tp2)
|
||||
if vp2 < 0 { return -1 }
|
||||
local rd = read_digits(json, vp2 + kv.length())
|
||||
if rd == "" { return -1 }
|
||||
print(_int_to_str(_str_to_int(ld) + _str_to_int(rd)))
|
||||
return rp
|
||||
}
|
||||
|
||||
// Fast value-pair extractor: find left/right then first value digits after each
|
||||
try_print_binop_value_pairs(json) {
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = json.indexOf(k_bo)
|
||||
if bpos < 0 { return -1 }
|
||||
local kl = "\"left\":"
|
||||
local kv = "\"value\":"
|
||||
local lp = index_of_from(json, kl, bpos)
|
||||
if lp < 0 { return -1 }
|
||||
local v1 = index_of_from(json, kv, lp)
|
||||
if v1 < 0 { return -1 }
|
||||
local ld = read_digits(json, v1 + kv.length())
|
||||
if ld == "" { return -1 }
|
||||
local rp = index_of_from(json, "\"right\":", lp)
|
||||
if rp < 0 { return -1 }
|
||||
local v2 = index_of_from(json, kv, rp)
|
||||
if v2 < 0 { return -1 }
|
||||
local rd = read_digits(json, v2 + kv.length())
|
||||
if rd == "" { return -1 }
|
||||
print(_int_to_str(_str_to_int(ld) + _str_to_int(rd)))
|
||||
return v2 + kv.length()
|
||||
}
|
||||
|
||||
// Minimal: Print(Compare) for integers. Prints 1/0 for true/false.
|
||||
// try_print_compare_at moved to MiniVmCompare
|
||||
// Extract first Print literal from JSON v0 Program and return its string representation
|
||||
parse_first_print_literal(json) {
|
||||
// Find a Print statement
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
local p = json.indexOf(k_print)
|
||||
if p < 0 { return null }
|
||||
// Find value type in the expression following Print
|
||||
local k_type = "\"type\":\""
|
||||
local tpos = json.indexOf(k_type)
|
||||
if tpos < 0 { return null }
|
||||
tpos = tpos + k_type.length()
|
||||
// Read type name until next quote
|
||||
local t_end = index_of_from(json, "\"", tpos)
|
||||
if t_end < 0 { return null }
|
||||
local ty = json.substring(tpos, t_end)
|
||||
// Find value field
|
||||
local k_val = "\"value\":"
|
||||
local vpos = index_of_from(json, k_val, t_end)
|
||||
if vpos < 0 { return null }
|
||||
vpos = vpos + k_val.length()
|
||||
if ty == "int" || ty == "i64" || ty == "integer" {
|
||||
// read digits via MiniJson
|
||||
local digits = new MiniJson().read_digits_from(json, vpos)
|
||||
return digits
|
||||
}
|
||||
if ty == "string" {
|
||||
// read quoted via MiniJson
|
||||
local i = index_of_from(json, "\"", vpos)
|
||||
if i < 0 { return null }
|
||||
return new MiniJson().read_quoted_from(json, i)
|
||||
}
|
||||
// Other types not supported yet
|
||||
return null
|
||||
}
|
||||
// helper: find balanced bracket range [ ... ] starting at idx (points to '[')
|
||||
find_balanced_array_end(json, idx) { return new MiniVmScan().find_balanced_array_end(json, idx) }
|
||||
// helper: find balanced object range { ... } starting at idx (points to '{')
|
||||
find_balanced_object_end(json, idx) { return new MiniVmScan().find_balanced_object_end(json, idx) }
|
||||
// Print all Print-Literal values within [start,end] (inclusive slice indices)
|
||||
print_prints_in_slice(json, start, end) { return new MiniVmPrints().print_prints_in_slice(json, start, end) }
|
||||
// Process top-level If with literal condition; print branch prints. Returns printed count.
|
||||
process_if_once(json) { return new MiniVmPrints().process_if_once(json) }
|
||||
print_all_print_literals(json) { return new MiniVmPrints().print_all_print_literals(json) }
|
||||
parse_first_int(json) {
|
||||
local key = "\"value\":{\"type\":\"int\",\"value\":"
|
||||
local idx = json.lastIndexOf(key)
|
||||
if idx < 0 { return "0" }
|
||||
local start = idx + key.length()
|
||||
return read_digits(json, start)
|
||||
}
|
||||
// Fallback: find first BinaryOp and return sum of two numeric values as string; empty if not found
|
||||
parse_first_binop_sum(json) {
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = json.indexOf(k_bo)
|
||||
if bpos < 0 { return "" }
|
||||
// typed pattern inside left/right.literal.value: {"type":"int","value":<digits>}
|
||||
local k_typed = "\"type\":\"int\",\"value\":"
|
||||
// first number
|
||||
local p1 = index_of_from(json, k_typed, bpos)
|
||||
if p1 < 0 { return "" }
|
||||
local d1 = read_digits(json, p1 + k_typed.length())
|
||||
if d1 == "" { return "" }
|
||||
// second number
|
||||
local p2 = index_of_from(json, k_typed, p1 + k_typed.length())
|
||||
if p2 < 0 { return "" }
|
||||
local d2 = read_digits(json, p2 + k_typed.length())
|
||||
if d2 == "" { return "" }
|
||||
return _int_to_str(_str_to_int(d1) + _str_to_int(d2))
|
||||
}
|
||||
// Linear pass: sum all numbers outside of quotes (fast, finite)
|
||||
sum_numbers_no_quotes(json) { return new MiniVmScan().sum_numbers_no_quotes(json) }
|
||||
// Naive: sum all digit runs anywhere (for simple BinaryOp JSON)
|
||||
sum_all_digits_naive(json) { return new MiniVmScan().sum_all_digits_naive(json) }
|
||||
// Sum first two integers outside quotes; returns string or empty if not found
|
||||
sum_first_two_numbers(json) { return new MiniVmScan().sum_first_two_numbers(json) }
|
||||
|
||||
// Sum two integers near a BinaryOp '+' token; bounded window to keep steps low
|
||||
sum_two_numbers_near_plus(json) {
|
||||
local k_plus = "\"operator\":\"+\""
|
||||
local op = json.indexOf(k_plus)
|
||||
if op < 0 { return "" }
|
||||
local n = json.length()
|
||||
local start = op - 120
|
||||
if start < 0 { start = 0 }
|
||||
local limit = op + 240
|
||||
if limit > n { limit = n }
|
||||
local i = start
|
||||
local found = 0
|
||||
local a = 0
|
||||
loop (i < limit) {
|
||||
local ch = json.substring(i, i+1)
|
||||
if ch == "\"" {
|
||||
// skip to next quote within window
|
||||
local j = index_of_from(json, "\"", i+1)
|
||||
if j < 0 || j > limit { break }
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
local d = read_digits(json, i)
|
||||
if d {
|
||||
if found == 0 {
|
||||
a = _str_to_int(d)
|
||||
found = 1
|
||||
} else {
|
||||
local b = _str_to_int(d)
|
||||
return _int_to_str(a + b)
|
||||
}
|
||||
i = i + d.length()
|
||||
continue
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return ""
|
||||
}
|
||||
// Fallback: sum all bare numbers (not inside quotes) in the JSON; return string or empty if none
|
||||
sum_all_numbers(json) {
|
||||
local cur = new MiniJson()
|
||||
local i = 0
|
||||
local n = json.length()
|
||||
local sum = 0
|
||||
loop (i < n) {
|
||||
local ch = json.substring(i, i+1)
|
||||
if ch == "\"" {
|
||||
// skip quoted string
|
||||
local s = cur.read_quoted_from(json, i)
|
||||
i = i + s.length() + 2
|
||||
continue
|
||||
}
|
||||
// try digits
|
||||
local d = cur.read_digits_from(json, i)
|
||||
if d != "" { sum = sum + _str_to_int(d) i = i + d.length() continue }
|
||||
i = i + 1
|
||||
}
|
||||
if sum == 0 { return "" }
|
||||
return _int_to_str(sum)
|
||||
}
|
||||
// (reserved) helper for future robust binop scan
|
||||
run(json) {
|
||||
// entry: attempt minimal quick shapes first, then broader routes
|
||||
// Quick path: Program-level Print of a single Literal string/int
|
||||
if json.indexOf("\"kind\":\"Program\"") >= 0 && json.indexOf("\"kind\":\"Print\"") >= 0 {
|
||||
// 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 }
|
||||
}
|
||||
}
|
||||
// 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()
|
||||
// digits until closing brace
|
||||
local ie = json.indexOf("}", ii)
|
||||
if ie < 0 { ie = ii }
|
||||
local d = json.substring(ii, ie)
|
||||
if d { print(d) return 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
// Single-purpose fast path for smoke: if BinaryOp '+' exists, try expression-bounded extractor first.
|
||||
if json.indexOf("\"BinaryOp\"") >= 0 && json.indexOf("\"operator\":\"+\"") >= 0 {
|
||||
// Bind to first Print and extract value×2 within expression bounds
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
local p = index_of_from(json, k_print, 0)
|
||||
if p >= 0 {
|
||||
local np0 = new MiniVmBinOp().try_print_binop_sum_expr_values(json, json.length(), p)
|
||||
if np0 > 0 { return 0 }
|
||||
}
|
||||
// Typed direct inside BinaryOp object (fast and finite)
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = json.indexOf(k_bo)
|
||||
if bpos >= 0 {
|
||||
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local li = index_of_from(json, k_lint, bpos)
|
||||
if li >= 0 {
|
||||
local ld = read_digits(json, li + k_lint.length())
|
||||
if ld != "" {
|
||||
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local ri = index_of_from(json, k_rint, li + k_lint.length())
|
||||
if ri >= 0 {
|
||||
local rd = read_digits(json, ri + k_rint.length())
|
||||
if rd != "" { print(_int_to_str(_str_to_int(ld) + _str_to_int(rd))) return 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// As a final bounded fallback under BinaryOp '+', sum first two numbers outside quotes
|
||||
{
|
||||
local s2 = sum_first_two_numbers(json)
|
||||
if s2 { print(s2) return 0 }
|
||||
}
|
||||
// (skip near-operator windowed scan to avoid high step counts under PyVM)
|
||||
}
|
||||
// Prefer If(literal) branch handling first
|
||||
local ifc = process_if_once(json)
|
||||
if ifc > 0 { return 0 }
|
||||
// Quick conservative path: if BinaryOp exists, sum bare numbers outside quotes
|
||||
// (limited to simple BinaryOp(int,int) JSON)
|
||||
if json.indexOf("\"BinaryOp\"") >= 0 {
|
||||
// Prefer expression-bounded scan first
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
local p = index_of_from(json, k_print, 0)
|
||||
if p >= 0 {
|
||||
// Deterministic: sum the first two numbers from successive 'value' fields
|
||||
local np0 = new MiniVmBinOp().try_print_binop_sum_expr_values(json, json.length(), p)
|
||||
if np0 > 0 { return 0 }
|
||||
}
|
||||
// Brace-free deterministic fallback tied to the first BinaryOp
|
||||
{
|
||||
local np1 = new MiniVmBinOp().try_print_binop_sum_after_bop(json)
|
||||
if np1 > 0 { return 0 }
|
||||
}
|
||||
// avoid global number-sum fallback to keep steps bounded
|
||||
}
|
||||
// 0) direct typed BinaryOp '+' fast-path (explicit left/right literal ints)
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local k_plus = "\"operator\":\"+\""
|
||||
if json.indexOf(k_bo) >= 0 && json.indexOf(k_plus) >= 0 {
|
||||
local np = try_print_binop_typed_direct(json)
|
||||
if np > 0 { return 0 }
|
||||
np = try_print_binop_typed_tokens(json)
|
||||
if np > 0 { return 0 }
|
||||
np = try_print_binop_value_pairs(json)
|
||||
if np > 0 { return 0 }
|
||||
// (skip bounded-window fallback around '+')
|
||||
}
|
||||
// 0) quick path: BinaryOp(int+int) typed fast-path
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = json.indexOf(k_bo)
|
||||
if bpos >= 0 {
|
||||
// typed left/right ints inside BinaryOp
|
||||
local k_lint = "\"left\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local li = index_of_from(json, k_lint, bpos)
|
||||
if li >= 0 {
|
||||
local ld = read_digits(json, li + k_lint.length())
|
||||
if ld != "" {
|
||||
local k_rint = "\"right\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":"
|
||||
local ri = index_of_from(json, k_rint, li + k_lint.length())
|
||||
if ri >= 0 {
|
||||
local rd = read_digits(json, ri + k_rint.length())
|
||||
if rd != "" {
|
||||
print(_int_to_str(_str_to_int(ld) + _str_to_int(rd)))
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fallback: sum two numeric values within the first Print.expression BinaryOp object
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
local p = index_of_from(json, k_print, 0)
|
||||
if p >= 0 {
|
||||
local k_expr = "\"expression\":{"
|
||||
local epos = index_of_from(json, k_expr, p)
|
||||
if epos > 0 {
|
||||
local obj_start = index_of_from(json, "{", epos)
|
||||
local obj_end = find_balanced_object_end(json, obj_start)
|
||||
if obj_start > 0 && obj_end > 0 {
|
||||
local k_bo2 = "\"kind\":\"BinaryOp\""
|
||||
local b2 = index_of_from(json, k_bo2, obj_start)
|
||||
if b2 > 0 && b2 < obj_end {
|
||||
local k_v = "\"value\":"
|
||||
local p1 = index_of_from(json, k_v, obj_start)
|
||||
local d1 = ""
|
||||
loop (p1 > 0 && p1 < obj_end) {
|
||||
d1 = new MiniJson().read_digits_from(json, p1 + k_v.length())
|
||||
if d1 != "" { break }
|
||||
p1 = index_of_from(json, k_v, p1 + k_v.length())
|
||||
}
|
||||
if d1 != "" {
|
||||
local p2 = index_of_from(json, k_v, p1 + k_v.length())
|
||||
local d2 = ""
|
||||
loop (p2 > 0 && p2 < obj_end) {
|
||||
d2 = new MiniJson().read_digits_from(json, p2 + k_v.length())
|
||||
if d2 != "" { break }
|
||||
p2 = index_of_from(json, k_v, p2 + k_v.length())
|
||||
}
|
||||
if d2 != "" {
|
||||
local ai = _str_to_int(d1)
|
||||
local bi = _str_to_int(d2)
|
||||
print(_int_to_str(ai + bi))
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fallback: parse-first within BinaryOp scope by scanning two numeric values
|
||||
local ssum = new MiniVmBinOp().parse_first_binop_sum(json)
|
||||
if ssum { print(ssum) return 0 }
|
||||
}
|
||||
// Attempt expression-local BinaryOp sum via existing helper on first Print
|
||||
{
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
local p = index_of_from(json, k_print, 0)
|
||||
if p >= 0 {
|
||||
local np = new MiniVmBinOp().try_print_binop_sum_any(json, json.length(), p)
|
||||
if np > 0 { return 0 }
|
||||
}
|
||||
}
|
||||
// 0-c) quick path: Compare(lhs int, rhs int)
|
||||
local k_cp = "\"kind\":\"Compare\""
|
||||
local cpos = json.indexOf(k_cp)
|
||||
if cpos >= 0 {
|
||||
// operation
|
||||
local k_op = "\"operation\":\""
|
||||
local opos = index_of_from(json, k_op, cpos)
|
||||
if opos > 0 {
|
||||
local oi = opos + k_op.length()
|
||||
local oj = index_of_from(json, "\"", oi)
|
||||
if oj > 0 {
|
||||
local op = json.substring(oi, oj)
|
||||
// lhs value
|
||||
local k_lhs = "\"lhs\":{\"kind\":\"Literal\""
|
||||
local hl = index_of_from(json, k_lhs, oj)
|
||||
if hl > 0 {
|
||||
local k_v = "\"value\":"
|
||||
local hv = index_of_from(json, k_v, hl)
|
||||
if hv > 0 {
|
||||
local a = read_digits(json, hv + k_v.length())
|
||||
// rhs value
|
||||
local k_rhs = "\"rhs\":{\"kind\":\"Literal\""
|
||||
local hr = index_of_from(json, k_rhs, hl)
|
||||
if hr > 0 {
|
||||
local rv = index_of_from(json, k_v, hr)
|
||||
if rv > 0 {
|
||||
local b = read_digits(json, rv + k_v.length())
|
||||
if a && b {
|
||||
local ai = _str_to_int(a)
|
||||
local bi = _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)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Scan global prints (flat programs)
|
||||
local pc = print_all_print_literals(json)
|
||||
// 2) as a robustness fallback, handle first BinaryOp sum within first Print.expression
|
||||
if pc == 0 {
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
local p = index_of_from(json, k_print, 0)
|
||||
if p >= 0 {
|
||||
local k_expr = "\"expression\":{"
|
||||
local epos = index_of_from(json, k_expr, p)
|
||||
if epos > 0 {
|
||||
local obj_start = index_of_from(json, "{", epos)
|
||||
local obj_end = find_balanced_object_end(json, obj_start)
|
||||
if obj_start > 0 && obj_end > 0 {
|
||||
local k_bo = "\"kind\":\"BinaryOp\""
|
||||
local bpos = index_of_from(json, k_bo, obj_start)
|
||||
if bpos > 0 && bpos < obj_end {
|
||||
// sum two numeric values inside this expression object
|
||||
local cur = new MiniJson()
|
||||
local k_v = "\"value\":"
|
||||
local p1 = index_of_from(json, k_v, obj_start)
|
||||
local d1 = ""
|
||||
loop (p1 > 0 && p1 < obj_end) {
|
||||
d1 = cur.read_digits_from(json, p1 + k_v.length())
|
||||
if d1 != "" { break }
|
||||
p1 = index_of_from(json, k_v, p1 + k_v.length())
|
||||
}
|
||||
if d1 != "" {
|
||||
local p2 = index_of_from(json, k_v, p1 + k_v.length())
|
||||
local d2 = ""
|
||||
loop (p2 > 0 && p2 < obj_end) {
|
||||
d2 = cur.read_digits_from(json, p2 + k_v.length())
|
||||
if d2 != "" { break }
|
||||
p2 = index_of_from(json, k_v, p2 + k_v.length())
|
||||
}
|
||||
if d2 != "" {
|
||||
local ai = _str_to_int(d1)
|
||||
local bi = _str_to_int(d2)
|
||||
print(_int_to_str(ai + bi))
|
||||
pc = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if pc == 0 {
|
||||
// last resort: typed pattern-wide sum, then safe number sum outside quotes, else single int literal
|
||||
local s = new MiniVmBinOp().parse_first_binop_sum(json)
|
||||
if s { print(s) } else {
|
||||
local ts = sum_numbers_no_quotes(json)
|
||||
if ts { print(ts) } else {
|
||||
local n = parse_first_int(json)
|
||||
print(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Pure helper: collect minimal print outputs (literals only) into an array
|
||||
collect_prints(json) {
|
||||
// Ported from self-contained smoke (Hardened minimal scanner)
|
||||
local out = new ArrayBox()
|
||||
local pos = 0
|
||||
local guard = 0
|
||||
// DEV trace: flip to 1 for one-run diagnosis; keep 0 for normal
|
||||
local trace = 0
|
||||
if trace == 1 { print("[collect][start] method entry") }
|
||||
local k_print = "\"kind\":\"Print\""
|
||||
loop (true) {
|
||||
guard = guard + 1
|
||||
if guard > 200 {
|
||||
if trace == 1 { print("[collect][guard_break] guard="+guard) }
|
||||
if trace == 1 { print("[collect][loop_exit] guard="+guard+" out.size="+out.size()) }
|
||||
if trace == 1 { print("[collect][return] out.size="+out.size()) }
|
||||
return out
|
||||
}
|
||||
local p = index_of_from(json, k_print, pos)
|
||||
if trace == 1 { print("[collect][loop] pos="+pos+" p="+p+" guard="+guard) }
|
||||
if p < 0 {
|
||||
if trace == 1 { print("[collect][p_break] p="+p) }
|
||||
if trace == 1 { print("[collect][loop_exit] guard="+guard+" out.size="+out.size()) }
|
||||
if trace == 1 { print("[collect][return] out.size="+out.size()) }
|
||||
return out
|
||||
}
|
||||
// bound current Print slice to [this, next)
|
||||
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 }
|
||||
if trace == 1 { print("[collect][p] "+p) print("[collect][next_p] "+next_p) print("[collect][slice_end] "+obj_end) }
|
||||
if trace == 1 {
|
||||
local k_expr = "\"expression\":{"
|
||||
local epos_dbg = index_of_from(json, k_expr, obj_start)
|
||||
print("[scan][expr] "+epos_dbg)
|
||||
local fc_dbg = index_of_from(json, "\"kind\":\"FunctionCall\"", obj_start)
|
||||
print("[scan][fc] "+fc_dbg)
|
||||
local bo_dbg = index_of_from(json, "\"kind\":\"BinaryOp\"", obj_start)
|
||||
print("[scan][bo] "+bo_dbg)
|
||||
local cp_dbg = index_of_from(json, "\"kind\":\"Compare\"", obj_start)
|
||||
print("[scan][cp] "+cp_dbg)
|
||||
local ts_dbg = index_of_from(json, "\"type\":\"string\"", obj_start)
|
||||
print("[scan][ts] "+ts_dbg)
|
||||
local ti_dbg = index_of_from(json, "\"type\":\"int\"", obj_start)
|
||||
print("[scan][ti] "+ti_dbg)
|
||||
// positions for tight patterns used by branches
|
||||
local ks_pat = "\"type\":\"string\",\"value\":\""
|
||||
print("[scan][ks] "+index_of_from(json, ks_pat, obj_start))
|
||||
local ki_pat = "\"type\":\"int\",\"value\":"
|
||||
print("[scan][ki] "+index_of_from(json, ki_pat, obj_start))
|
||||
}
|
||||
|
||||
// 1) FunctionCall echo/itoa (single literal or empty args)
|
||||
{
|
||||
// Limit search within Print.expression object for stability
|
||||
local k_expr = "\"expression\":{"
|
||||
local epos = index_of_from(json, k_expr, obj_start)
|
||||
if epos > 0 { if epos < obj_end {
|
||||
local expr_start = index_of_from(json, "{", epos)
|
||||
if expr_start > 0 { if expr_start < obj_end {
|
||||
local expr_end = new MiniVmScan().find_balanced_object_end(json, expr_start)
|
||||
if expr_end > 0 { if expr_end <= obj_end {
|
||||
if trace == 1 { print("[collect][expr] "+expr_start+","+expr_end) }
|
||||
local k_fc = "\"kind\":\"FunctionCall\""
|
||||
local fcp = index_of_from(json, k_fc, expr_start)
|
||||
if trace == 1 { print("[collect][fc_in_expr] "+fcp+" (bounds "+expr_start+","+expr_end+")") }
|
||||
if fcp > 0 { if fcp < expr_end {
|
||||
if trace == 1 { print("[collect][fc_found] entering function call handler") }
|
||||
local kn = "\"name\":\""
|
||||
local np = index_of_from(json, kn, fcp)
|
||||
if trace == 1 { print("[collect][name_search] "+np+" (obj_end "+obj_end+")") }
|
||||
if np > 0 { if np < obj_end {
|
||||
local ni = np + kn.length()
|
||||
local nj = index_of_from(json, "\"", ni)
|
||||
if trace == 1 { print("[collect][name_bounds] "+ni+","+nj+" (expr_end "+expr_end+")") }
|
||||
if nj > 0 { if nj <= expr_end {
|
||||
local fname = json.substring(ni, nj)
|
||||
if trace == 1 { print("[collect][fname] '"+fname+"'") }
|
||||
local ka = "\"arguments\":["
|
||||
local ap = index_of_from(json, ka, nj)
|
||||
if ap <= 0 {
|
||||
local ka0 = "\"arguments\":"
|
||||
local ap0 = index_of_from(json, ka0, nj)
|
||||
if ap0 >= 0 { if ap0 < expr_end { ap = ap0 } }
|
||||
}
|
||||
if ap >= 0 { if ap < expr_end {
|
||||
// detect empty args [] quickly: no type token inside balanced array
|
||||
local arr_start = index_of_from(json, "[", ap)
|
||||
if arr_start >= 0 { if arr_start < expr_end {
|
||||
local arr_end = new MiniVmScan().find_balanced_array_end(json, arr_start)
|
||||
if arr_end >= 0 { if arr_end <= expr_end {
|
||||
local kt = "\"type\":\""
|
||||
local atpos = index_of_from(json, kt, arr_start)
|
||||
if trace == 1 { print("[collect][empty_check] atpos="+atpos+" arr_bounds=["+arr_start+","+arr_end+"]") }
|
||||
if atpos < 0 || atpos >= arr_end {
|
||||
if trace == 1 { print("[collect][empty_args] fname='"+fname+"'") }
|
||||
if fname == "echo" { out.push("") pos = obj_end continue }
|
||||
if fname == "itoa" { out.push("0") pos = obj_end continue }
|
||||
}
|
||||
}}}}
|
||||
// string arg
|
||||
local ks = "\"type\":\"string\",\"value\":\""
|
||||
local ps = index_of_from(json, ks, ap)
|
||||
if ps > 0 { if ps < expr_end {
|
||||
local si = ps + ks.length()
|
||||
local sj = index_of_from(json, "\"", si)
|
||||
if sj > 0 { if sj <= expr_end {
|
||||
local sval = json.substring(si, sj)
|
||||
if fname == "echo" { out.push(sval) pos = obj_end + 1 continue }
|
||||
}}
|
||||
}}
|
||||
// int arg
|
||||
local ki = "\"type\":\"int\",\"value\":"
|
||||
local pi = index_of_from(json, ki, ap)
|
||||
if pi > 0 { if pi < expr_end {
|
||||
local ival = read_digits(json, pi + ki.length())
|
||||
if ival != "" { if fname == "itoa" { out.push(ival) pos = obj_end + 1 continue } else { if fname == "echo" { out.push(ival) pos = obj_end + 1 continue } } }
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}}}
|
||||
}}
|
||||
}}
|
||||
}
|
||||
|
||||
// 2) 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 {
|
||||
if index_of_from(json, "\"operator\":\"+\"", bpos) > 0 {
|
||||
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 != "" { if trace == 1 { print("[hit][bo-typed] "+ld+"+"+rd) } out.push(_int_to_str(_str_to_int(ld) + _str_to_int(rd))) pos = p + k_print.length() continue }
|
||||
}}
|
||||
}
|
||||
}}
|
||||
// fallback: 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 = new MiniJson().read_digits_from(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 = new MiniJson().read_digits_from(json, v2 + k_v.length())
|
||||
if d2 != "" { if trace == 1 { print("[hit][bo-fallback] "+d1+"+"+d2) } out.push(_int_to_str(_str_to_int(d1) + _str_to_int(d2))) pos = p + k_print.length() continue }
|
||||
}}
|
||||
}
|
||||
}}
|
||||
}
|
||||
}}
|
||||
}}
|
||||
}
|
||||
|
||||
// 3) Compare(lhs/rhs ints)
|
||||
{
|
||||
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
|
||||
}
|
||||
}}
|
||||
}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}
|
||||
|
||||
// (FunctionCall branch moved earlier)
|
||||
|
||||
// 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 {
|
||||
if trace == 1 { print("[hit][str]") }
|
||||
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 != "" { if trace == 1 { print("[hit][i-lit] "+digits) } out.push(digits) pos = p + k_print.length() continue }
|
||||
}}
|
||||
}
|
||||
// Unknown: skip this Print object entirely to avoid stalls and mis-detection
|
||||
// Use coarse slice end (next Print position) when available; fallback to k_print-length step
|
||||
pos = obj_end + 1
|
||||
if pos <= p { pos = p + k_print.length() }
|
||||
}
|
||||
if trace == 1 { print("[collect][loop_exit] guard="+guard+" out.size="+out.size()) }
|
||||
if trace == 1 { print("[collect][return] out.size="+out.size()) }
|
||||
return out
|
||||
}
|
||||
}
|
||||
@ -1,531 +0,0 @@
|
||||
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())
|
||||
}
|
||||
}
|
||||
@ -1,112 +0,0 @@
|
||||
// mir_vm_m2.nyash — Ny製の最小MIR(JSON v0)実行器(M2: const/binop/ret)
|
||||
|
||||
static box MirVmM2 {
|
||||
_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
|
||||
}
|
||||
_int_to_str(n) {
|
||||
if n == 0 { return "0" }
|
||||
local v = n
|
||||
local out = ""
|
||||
loop (v > 0) {
|
||||
local d = v % 10
|
||||
local ch = "0"
|
||||
if d == 1 { ch = "1" } else { if d == 2 { ch = "2" } else { if d == 3 { ch = "3" } else { if d == 4 { ch = "4" } else { if d == 5 { ch = "5" } else { if d == 6 { ch = "6" } else { if d == 7 { ch = "7" } else { if d == 8 { ch = "8" } else { if d == 9 { ch = "9" } } } } } } } }
|
||||
out = ch + out
|
||||
v = v / 10
|
||||
}
|
||||
return out
|
||||
}
|
||||
_find_int_in(seg, keypat) {
|
||||
local p = seg.indexOf(keypat)
|
||||
if p < 0 { return null }
|
||||
p = p + keypat.length()
|
||||
local i = p
|
||||
local out = ""
|
||||
loop(true) {
|
||||
local ch = seg.substring(i, i+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" { out = out + ch i = i + 1 } else { break }
|
||||
}
|
||||
if out == "" { return null }
|
||||
return me._str_to_int(out)
|
||||
}
|
||||
_find_str_in(seg, keypat) {
|
||||
local p = seg.indexOf(keypat)
|
||||
if p < 0 { return "" }
|
||||
p = p + keypat.length()
|
||||
local q = seg.indexOf(""", p)
|
||||
if q < 0 { return "" }
|
||||
return seg.substring(p, q)
|
||||
}
|
||||
_get(regs, id) { if regs.has(id) { return regs.get(id) } return 0 }
|
||||
_set(regs, id, v) { regs.set(id, v) }
|
||||
_bin(kind, a, b) {
|
||||
if kind == "Add" { return a + b }
|
||||
if kind == "Sub" { return a - b }
|
||||
if kind == "Mul" { return a * b }
|
||||
if kind == "Div" { if b == 0 { return 0 } else { return a / b } }
|
||||
return 0
|
||||
}
|
||||
run(json) {
|
||||
local regs = new MapBox()
|
||||
local pos = json.indexOf(""instructions":[")
|
||||
if pos < 0 {
|
||||
print("0")
|
||||
return 0
|
||||
}
|
||||
local cur = pos
|
||||
loop(true) {
|
||||
local op_pos = json.indexOf(""op":"", cur)
|
||||
if op_pos < 0 { break }
|
||||
local name_start = op_pos + 6
|
||||
local name_end = json.indexOf(""", name_start)
|
||||
if name_end < 0 { break }
|
||||
local opname = json.substring(name_start, name_end)
|
||||
local next_pos = json.indexOf(""op":"", name_end)
|
||||
if next_pos < 0 { next_pos = json.length() }
|
||||
local seg = json.substring(op_pos, next_pos)
|
||||
if opname == "const" {
|
||||
local dst = me._find_int_in(seg, ""dst":")
|
||||
local val = me._find_int_in(seg, ""value":{"type":"i64","value":")
|
||||
if dst != null and val != null { me._set(regs, "" + dst, val) }
|
||||
} else { if opname == "binop" {
|
||||
local dst = me._find_int_in(seg, ""dst":")
|
||||
local kind = me._find_str_in(seg, ""op_kind":"")
|
||||
local lhs = me._find_int_in(seg, ""lhs":")
|
||||
local rhs = me._find_int_in(seg, ""rhs":")
|
||||
if dst != null and lhs != null and rhs != null {
|
||||
local a = me._get(regs, "" + lhs)
|
||||
local b = me._get(regs, "" + rhs)
|
||||
me._set(regs, "" + dst, me._bin(kind, a, b))
|
||||
}
|
||||
} else { if opname == "ret" {
|
||||
local v = me._find_int_in(seg, ""value":")
|
||||
if v == null { v = 0 }
|
||||
local out = me._get(regs, "" + v)
|
||||
print(me._int_to_str(out))
|
||||
return 0
|
||||
} } }
|
||||
cur = next_pos
|
||||
}
|
||||
print("0")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,309 +0,0 @@
|
||||
// mir_vm_min.nyash — Ny製の最小MIR(JSON v0)実行器(const→retのみ)
|
||||
// 目的: M2スケルトン。仕様は既定OFFに影響しない新規アプリのみ。
|
||||
// 入力: MIR(JSON v0) 文字列。形式例:
|
||||
// {
|
||||
// "functions":[{"name":"main","params":[],"blocks":[{"id":0,"instructions":[
|
||||
// {"op":"const","dst":1,"value":{"type":"i64","value":42}},
|
||||
// {"op":"ret","value":1}
|
||||
// ]}]}]
|
||||
// }
|
||||
// 振る舞い:
|
||||
// - M1: 最初の const i64 の値を読み取り print
|
||||
// - M2: const/binop/compare/ret を最小実装(簡易スキャンで安全に解釈)
|
||||
|
||||
static box MirVmMin {
|
||||
// Public entry used by parity tests (calls into minimal runner)
|
||||
run(mjson) {
|
||||
local v = me._run_min(mjson)
|
||||
print(me._int_to_str(v))
|
||||
return v
|
||||
}
|
||||
// 最小限のスキャン関数(依存ゼロ版)
|
||||
index_of_from(hay, needle, pos) {
|
||||
if pos < 0 { pos = 0 }
|
||||
local n = hay.length()
|
||||
if pos >= n { return -1 }
|
||||
local m = needle.length()
|
||||
if m <= 0 { return pos }
|
||||
local i = pos
|
||||
local limit = n - m
|
||||
loop (i <= limit) {
|
||||
local seg = hay.substring(i, i + m)
|
||||
if seg == needle { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
read_digits(text, pos) {
|
||||
local out = ""
|
||||
local i = pos
|
||||
loop (true) {
|
||||
local s = text.substring(i, i+1)
|
||||
if s == "" { break }
|
||||
if s == "0" || s == "1" || s == "2" || s == "3" || s == "4" || s == "5" || s == "6" || s == "7" || s == "8" || s == "9" {
|
||||
out = out + s
|
||||
i = i + 1
|
||||
} else { break }
|
||||
}
|
||||
return out
|
||||
}
|
||||
_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
|
||||
}
|
||||
_int_to_str(n) {
|
||||
if n == 0 { return "0" }
|
||||
local v = n
|
||||
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
|
||||
}
|
||||
|
||||
// MVP: 最初の const i64 の値を抽出
|
||||
_extract_first_const_i64(text) {
|
||||
if text == null { return 0 }
|
||||
// "op":"const" を探す
|
||||
local p = text.indexOf("\"op\":\"const\"")
|
||||
if p < 0 { return 0 }
|
||||
// そこから "\"value\":{\"type\":\"i64\",\"value\":" を探す
|
||||
local key = "\"value\":{\"type\":\"i64\",\"value\":"
|
||||
local q = me.index_of_from(text, key, p)
|
||||
if q < 0 { return 0 }
|
||||
q = q + key.length()
|
||||
// 連続する数字を読む
|
||||
local digits = me.read_digits(text, q)
|
||||
if digits == "" { return 0 }
|
||||
return me._str_to_int(digits)
|
||||
}
|
||||
|
||||
// --- M2 追加: 最小 MIR 実行(const/binop/compare/ret) ---
|
||||
_get_map(regs, key) { if regs.has(key) { return regs.get(key) } return 0 }
|
||||
_set_map(regs, key, val) { regs.set(key, val) }
|
||||
_find_int_in(seg, keypat) {
|
||||
local p = seg.indexOf(keypat)
|
||||
if p < 0 { return null }
|
||||
p = p + keypat.length()
|
||||
local i = p
|
||||
local out = ""
|
||||
loop(true) {
|
||||
local ch = seg.substring(i, i+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" { out = out + ch i = i + 1 } else { break }
|
||||
}
|
||||
if out == "" { return null }
|
||||
return me._str_to_int(out)
|
||||
}
|
||||
_find_str_in(seg, keypat) {
|
||||
local p = seg.indexOf(keypat)
|
||||
if p < 0 { return "" }
|
||||
p = p + keypat.length()
|
||||
local q = me.index_of_from(seg, "\"", p)
|
||||
if q < 0 { return "" }
|
||||
return seg.substring(p, q)
|
||||
}
|
||||
// --- JSON segment helpers (brace/bracket aware, minimal) ---
|
||||
_seek_obj_start(text, from_pos) {
|
||||
// scan backward to the nearest '{'
|
||||
local i = from_pos
|
||||
loop(true) {
|
||||
i = i - 1
|
||||
if i < 0 { return 0 }
|
||||
local ch = text.substring(i, i+1)
|
||||
if ch == "{" { return i }
|
||||
}
|
||||
return 0
|
||||
}
|
||||
_seek_obj_end(text, obj_start) {
|
||||
// starting at '{', find matching '}' using depth counter
|
||||
local i = obj_start
|
||||
local depth = 0
|
||||
loop(true) {
|
||||
local ch = text.substring(i, i+1)
|
||||
if ch == "" { break }
|
||||
if ch == "{" { depth = depth + 1 }
|
||||
else { if ch == "}" { depth = depth - 1 } }
|
||||
if depth == 0 { return i + 1 }
|
||||
i = i + 1
|
||||
}
|
||||
return i
|
||||
}
|
||||
_seek_array_end(text, array_after_bracket_pos) {
|
||||
// given pos right after '[', find the matching ']'
|
||||
local i = array_after_bracket_pos
|
||||
local depth = 1
|
||||
loop(true) {
|
||||
local ch = text.substring(i, i+1)
|
||||
if ch == "" { break }
|
||||
if ch == "[" { depth = depth + 1 }
|
||||
else { if ch == "]" { depth = depth - 1 } }
|
||||
if depth == 0 { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return i
|
||||
}
|
||||
_map_binop_symbol(sym) {
|
||||
if sym == "+" { return "Add" }
|
||||
if sym == "-" { return "Sub" }
|
||||
if sym == "*" { return "Mul" }
|
||||
if sym == "/" { return "Div" }
|
||||
if sym == "%" { return "Mod" }
|
||||
return "" }
|
||||
_map_cmp_symbol(sym) {
|
||||
if sym == "==" { return "Eq" }
|
||||
if sym == "!=" { return "Ne" }
|
||||
if sym == "<" { return "Lt" }
|
||||
if sym == "<=" { return "Le" }
|
||||
if sym == ">" { return "Gt" }
|
||||
if sym == ">=" { return "Ge" }
|
||||
return "" }
|
||||
_eval_binop(kind, a, b) {
|
||||
if kind == "Add" { return a + b }
|
||||
if kind == "Sub" { return a - b }
|
||||
if kind == "Mul" { return a * b }
|
||||
if kind == "Div" { if b == 0 { return 0 } else { return a / b } }
|
||||
if kind == "Mod" { if b == 0 { return 0 } else { return a % b } }
|
||||
return 0 }
|
||||
_eval_cmp(kind, a, b) {
|
||||
if kind == "Eq" { if a == b { return 1 } else { return 0 } }
|
||||
if kind == "Ne" { if a != b { return 1 } else { return 0 } }
|
||||
if kind == "Lt" { if a < b { return 1 } else { return 0 } }
|
||||
if kind == "Gt" { if a > b { return 1 } else { return 0 } }
|
||||
if kind == "Le" { if a <= b { return 1 } else { return 0 } }
|
||||
if kind == "Ge" { if a >= b { return 1 } else { return 0 } }
|
||||
return 0
|
||||
}
|
||||
// Locate start of instructions array for given block id
|
||||
_block_insts_start(mjson, bid) {
|
||||
local key = "\"id\":" + me._int_to_str(bid)
|
||||
local p = mjson.indexOf(key)
|
||||
if p < 0 { return -1 }
|
||||
local q = me.index_of_from(mjson, "\"instructions\":[", p)
|
||||
if q < 0 { return -1 }
|
||||
// "\"instructions\":[" is 16 chars → return pos right after '['
|
||||
return q + 16
|
||||
}
|
||||
_block_insts_end(mjson, insts_start) {
|
||||
// Bound to the end bracket of this block's instructions array
|
||||
return me._seek_array_end(mjson, insts_start)
|
||||
}
|
||||
_run_min(mjson) {
|
||||
local regs = new MapBox()
|
||||
// Control flow: start at block 0, process until ret
|
||||
local bb = 0
|
||||
loop(true) {
|
||||
local pos = me._block_insts_start(mjson, bb)
|
||||
if pos < 0 { return me._extract_first_const_i64(mjson) }
|
||||
local block_end = me._block_insts_end(mjson, pos)
|
||||
// Single-pass over instructions: segment each op object precisely and evaluate
|
||||
local scan = pos
|
||||
local moved = 0
|
||||
loop(true) {
|
||||
// find next op field within this block
|
||||
local opos = me.index_of_from(mjson, "\"op\":\"", scan)
|
||||
if opos < 0 || opos >= block_end { break }
|
||||
// find exact JSON object bounds for this instruction
|
||||
// Determine object start as the last '{' between pos..opos
|
||||
local i = pos
|
||||
local obj_start = opos
|
||||
loop(i <= opos) {
|
||||
local ch0 = mjson.substring(i, i+1)
|
||||
if ch0 == "{" { obj_start = i }
|
||||
i = i + 1
|
||||
}
|
||||
local obj_end = me._seek_obj_end(mjson, obj_start)
|
||||
if obj_end > block_end { obj_end = block_end }
|
||||
local seg = mjson.substring(obj_start, obj_end)
|
||||
|
||||
// dispatch by op name (v0/v1 tolerant)
|
||||
local opname = me._find_str_in(seg, "\"op\":\"")
|
||||
if opname == "const" {
|
||||
local cdst = me._find_int_in(seg, "\"dst\":")
|
||||
local cval = me._find_int_in(seg, "\"value\":{\"type\":\"i64\",\"value\":")
|
||||
if cdst != null and cval != null { me._set_map(regs, "" + cdst, cval) }
|
||||
} else {
|
||||
if opname == "binop" {
|
||||
local bdst = me._find_int_in(seg, "\"dst\":")
|
||||
local bkind = me._find_str_in(seg, "\"op_kind\":\"")
|
||||
if bkind == "" { bkind = me._map_binop_symbol(me._find_str_in(seg, "\"operation\":\"")) }
|
||||
local blhs = me._find_int_in(seg, "\"lhs\":")
|
||||
local brhs = me._find_int_in(seg, "\"rhs\":")
|
||||
if bdst != null and blhs != null and brhs != null {
|
||||
local a = me._get_map(regs, "" + blhs)
|
||||
local b = me._get_map(regs, "" + brhs)
|
||||
local r = me._eval_binop(bkind, a, b)
|
||||
me._set_map(regs, "" + bdst, r)
|
||||
}
|
||||
} else {
|
||||
if opname == "compare" {
|
||||
local kdst = me._find_int_in(seg, "\"dst\":")
|
||||
local kkind = me._find_str_in(seg, "\"cmp\":\"")
|
||||
if kkind == "" { kkind = me._map_cmp_symbol(me._find_str_in(seg, "\"operation\":\"")) }
|
||||
local klhs = me._find_int_in(seg, "\"lhs\":")
|
||||
local krhs = me._find_int_in(seg, "\"rhs\":")
|
||||
if kdst != null and klhs != null and krhs != null {
|
||||
local a = me._get_map(regs, "" + klhs)
|
||||
local b = me._get_map(regs, "" + krhs)
|
||||
local r = me._eval_cmp(kkind, a, b)
|
||||
me._set_map(regs, "" + kdst, r)
|
||||
}
|
||||
} else {
|
||||
if opname == "jump" {
|
||||
local tgt = me._find_int_in(seg, "\"target\":")
|
||||
if tgt != null { bb = tgt scan = block_end moved = 1 break }
|
||||
} else {
|
||||
if opname == "branch" {
|
||||
local cond = me._find_int_in(seg, "\"cond\":")
|
||||
local then_id = me._find_int_in(seg, "\"then\":")
|
||||
local else_id = me._find_int_in(seg, "\"else\":")
|
||||
local cval = 0
|
||||
if cond != null { cval = me._get_map(regs, "" + cond) }
|
||||
if cval != 0 { bb = then_id } else { bb = else_id }
|
||||
scan = block_end
|
||||
moved = 1
|
||||
break
|
||||
} else {
|
||||
if opname == "ret" {
|
||||
local rv = me._find_int_in(seg, "\"value\":")
|
||||
if rv == null { rv = 0 }
|
||||
return me._get_map(regs, "" + rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// advance to the end of this instruction object
|
||||
scan = obj_end
|
||||
}
|
||||
// No ret encountered in this block; if control moved, continue with new bb
|
||||
if moved == 1 { continue }
|
||||
// Fallback when ret not found at all in processed blocks
|
||||
return me._extract_first_const_i64(mjson)
|
||||
}
|
||||
return me._extract_first_const_i64(mjson)
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,249 +0,0 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
// vm_kernel_box.nyash — NYABI Kernel (skeleton, dev-only; not wired)
|
||||
// Scope: Provide policy/decision helpers behind an explicit OFF toggle.
|
||||
// Notes: This box is not referenced by the VM by default.
|
||||
|
||||
static box VmKernelBox {
|
||||
// Report version and supported features.
|
||||
caps() {
|
||||
// v0 draft: features are informative only.
|
||||
return "{\"version\":0,\"features\":[\"policy\"]}"
|
||||
}
|
||||
|
||||
// Decide stringify strategy for a given type.
|
||||
// Returns: "direct" | "rewrite_stringify" | "fallback"
|
||||
stringify_policy(typeName) {
|
||||
if typeName == "VoidBox" { return "rewrite_stringify" }
|
||||
return "fallback"
|
||||
}
|
||||
|
||||
// Decide equals strategy for two types.
|
||||
// Returns: "object" | "value" | "fallback"
|
||||
equals_policy(lhsType, rhsType) {
|
||||
if lhsType == rhsType { return "value" }
|
||||
return "fallback"
|
||||
}
|
||||
|
||||
// Batch resolve method dispatch plans.
|
||||
// Input/Output via tiny JSON strings (draft). Returns "{\"plans\":[]}" for now.
|
||||
resolve_method_batch(reqs_json) {
|
||||
return "{\"plans\":[]}"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
using selfhost.vm.boxes.mini_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)
|
||||
print("DEBUG: arr.size=" + arr.size())
|
||||
local i = 0
|
||||
loop (i < arr.size()) { print(arr.get(i)) i = i + 1 }
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@ -1,282 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
using selfhost.vm.boxes.mini_vm_core as MiniVm
|
||||
using selfhost.vm.boxes.mini_vm_prints as MiniVmPrints
|
||||
|
||||
static box Main {
|
||||
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}}}]}"
|
||||
|
||||
new MiniVmPrints().print_prints_in_slice(json, 0, json.length())
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
// MiniJsonLoader (Stage-B scaffold)
|
||||
// Purpose: centralize minimal JSON cursor ops for Mini-VM.
|
||||
// Implementation note: For now we delegate to local MiniJsonCur-compatible
|
||||
// helpers. In a later step, this can be swapped to use `apps/libs/json_cur.nyash`
|
||||
// (JsonCursorBox) without touching Mini-VM call sites.
|
||||
|
||||
static box MiniJsonLoader {
|
||||
read_quoted_from(s, pos) {
|
||||
// Local fallback (same behavior as MiniJsonCur.read_quoted_from)
|
||||
// Keep in sync with Mini-VM until libs adoption gate is enabled.
|
||||
local i = pos
|
||||
if s.substring(i, i+1) != "\"" { return "" }
|
||||
i = i + 1
|
||||
local out = ""
|
||||
local n = s.length()
|
||||
loop (i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch == "\"" { break }
|
||||
if ch == "\\" { i = i + 1 ch = s.substring(i, i+1) }
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
read_digits_from(s, pos) {
|
||||
local out = ""
|
||||
local i = pos
|
||||
if i == null { return out }
|
||||
if i < 0 { return out }
|
||||
loop (true) {
|
||||
local ch = s.substring(i, i+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" {
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
} else { break }
|
||||
}
|
||||
return out
|
||||
}
|
||||
next_non_ws(s, pos) {
|
||||
local i = pos
|
||||
local n = s.length()
|
||||
loop (i < n) {
|
||||
local ch = s.substring(i, i+1)
|
||||
if ch != " " && ch != "\n" && ch != "\r" && ch != "\t" { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
// Thin entry: delegate to core MiniVm
|
||||
// Using is pre-inlined by runner; keep entry minimal for maintainability.
|
||||
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 } } }
|
||||
return new MiniVm().run(json)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
// Mini-VM: function-based entry for branching
|
||||
// Local static box (duplicated from mini_vm_lib for now to avoid include gate issues)
|
||||
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) {
|
||||
@out = ""
|
||||
loop (true) {
|
||||
@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) {
|
||||
@key = "\"value\":{\"type\":\"int\",\"value\":"
|
||||
@idx = json.lastIndexOf(key)
|
||||
if idx < 0 { return "0" }
|
||||
@start = idx + key.length()
|
||||
return read_digits(json, start)
|
||||
}
|
||||
run_branch(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
|
||||
}
|
||||
}
|
||||
|
||||
// Program entry: embedded JSON (value=1 → print 10; else → 20)
|
||||
static box Main {
|
||||
main(args) {
|
||||
@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 {
|
||||
@s = args.get(0)
|
||||
if s { json = s }
|
||||
}
|
||||
}
|
||||
return vm.run_branch(json)
|
||||
}
|
||||
}
|
||||
|
||||
// Top-level fallback entry for current runner
|
||||
function main(args) {
|
||||
@vm = new MiniVm()
|
||||
@json = "{\"kind\":\"Program\",\"statements\":[{\"kind\":\"Print\",\"expression\":{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":1}}}]}"
|
||||
if args {
|
||||
if args.size() > 0 {
|
||||
@s = args.get(0)
|
||||
if s { json = s }
|
||||
}
|
||||
}
|
||||
@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
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
// Mini-VM library (function-based) with a tiny JSON extractor
|
||||
// Safe MVP: no real JSON parsing; string scan for first int literal only
|
||||
|
||||
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 consecutive digits starting at pos
|
||||
read_digits(json, pos) {
|
||||
@out = ""
|
||||
loop (true) {
|
||||
@s = json.substring(pos, pos+1)
|
||||
if s == "" { break }
|
||||
if _is_digit(s) { out = out + s pos = pos + 1 } else { break }
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Extract the first integer literal from our AST JSON v0 subset
|
||||
parse_first_int(json) {
|
||||
@key = "\"value\":{\"type\":\"int\",\"value\":"
|
||||
@idx = json.lastIndexOf(key)
|
||||
if idx < 0 { return "0" }
|
||||
@start = idx + key.length()
|
||||
return read_digits(json, start)
|
||||
}
|
||||
|
||||
// Execute a minimal program: print the extracted integer and exit code 0
|
||||
run(json) {
|
||||
@n = parse_first_int(json)
|
||||
print(n)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
// mir_min_entry.nyash — MirVmMin の薄いエントリ
|
||||
// 引数があれば JSON を第1引数から受け取る。無ければデフォルトの const→ret (42)。
|
||||
|
||||
using selfhost.vm.mir_min as MirVmMin
|
||||
|
||||
static box Main {
|
||||
main(args) {
|
||||
// 既定の最小 MIR(JSON v0)
|
||||
local json = "{\"functions\":[{\"name\":\"main\",\"params\":[],\"blocks\":[{\"id\":0,\"instructions\":[{\"op\":\"const\",\"dst\":1,\"value\":{\"type\":\"i64\",\"value\":42}},{\"op\":\"ret\",\"value\":1}]}]}]}"
|
||||
if args != null { if args.size() > 0 { local s = args.get(0) if s != null { json = s } } }
|
||||
local v = MirVmMin._run_min(json)
|
||||
print(MirVmMin._int_to_str(v))
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user