Remove legacy apps/selfhost tree; intentionally break remaining references to surface migrations (moving to lang/)

This commit is contained in:
nyash-codex
2025-11-01 18:54:36 +09:00
parent fcf28be8f9
commit f1a35be9f2
51 changed files with 0 additions and 5628 deletions

View File

@ -1,20 +0,0 @@
# SelfHosting Apps (Nyonly)
Purpose
- Keep selfhosting 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 crossproject coupling.
- If moving an existing `apps/*` item here, update docs/scripts accordingly.
- For namespace usage, pass `--using-path apps/selfhost:apps`.

View File

@ -1,19 +0,0 @@
// Adapter for JSON cursor operations (extracted)
// Wraps MiniJsonCur and exposes a stable facade
using selfhost.vm.json_cur as MiniJsonCur
static box MiniJson {
read_quoted_from(s, pos) {
local cur = new MiniJsonCur()
return cur.read_quoted_from(s, pos)
}
read_digits_from(s, pos) {
local cur = new MiniJsonCur()
return cur.read_digits_from(s, pos)
}
next_non_ws(s, pos) {
local cur = new MiniJsonCur()
return cur.next_non_ws(s, pos)
}
}

View File

@ -1,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))
}
}

View File

@ -1,47 +0,0 @@
using selfhost.vm.scan as MiniVmScan
static box MiniVmCompare {
// Compare(lhs int, rhs int) minimal: prints 0/1 and returns next pos or -1
try_print_compare_at(json, end, print_pos) {
local scan = new MiniVmScan()
local k_cp = "\"kind\":\"Compare\""
local cpos = scan.index_of_from(json, k_cp, print_pos)
if cpos <= 0 || cpos >= end { return -1 }
local k_op = "\"operation\":\""
local opos = scan.index_of_from(json, k_op, cpos)
if opos <= 0 || opos >= end { return -1 }
local oi = opos + k_op.length()
local oj = scan.index_of_from(json, "\"", oi)
if oj <= 0 || oj > end { return -1 }
local op = json.substring(oi, oj)
// lhs value
local k_lhs = "\"lhs\":{\"kind\":\"Literal\""
local hl = scan.index_of_from(json, k_lhs, oj)
if hl <= 0 || hl >= end { return -1 }
local k_v = "\"value\":"
local hv = scan.index_of_from(json, k_v, hl)
if hv <= 0 || hv >= end { return -1 }
local a = scan.read_digits(json, hv + k_v.length())
// rhs value
local k_rhs = "\"rhs\":{\"kind\":\"Literal\""
local hr = scan.index_of_from(json, k_rhs, hl)
if hr <= 0 || hr >= end { return -1 }
local rv = scan.index_of_from(json, k_v, hr)
if rv <= 0 || rv >= end { return -1 }
local b = scan.read_digits(json, rv + k_v.length())
if !a || !b { return -1 }
local ai = scan._str_to_int(a)
local bi = scan._str_to_int(b)
local res = 0
if op == "<" { if ai < bi { res = 1 } }
if op == "==" { if ai == bi { res = 1 } }
if op == "<=" { if ai <= bi { res = 1 } }
if op == ">" { if ai > bi { res = 1 } }
if op == ">=" { if ai >= bi { res = 1 } }
if op == "!=" { if ai != bi { res = 1 } }
print(res)
// advance after rhs object (coarsely)
return rv + 1
}
}

View File

@ -1,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 ""
}
}

View File

@ -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 Stage2)
- `mir/`: builder/optimizer stubs (future; current target is JSON v0 emit)
- `tests/`: Stage1/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.
- Stage3 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`.

View File

@ -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 }
}

View File

@ -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 }
}

View File

@ -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 } }

View File

@ -1,917 +0,0 @@
// ParserBox — Stage1 JSON v0 generatorextracted, 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 Stage1 beyond string literal parse above
// using metadata omitted in Stage1
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} (Stage2 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 } }

View File

@ -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 Stage15)
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 Stage1 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
}
}

View File

@ -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}}]}"
}
}

View File

@ -1,5 +0,0 @@
static box MirBuilder {
// placeholder
main(args) { return 0 }
}

View File

@ -1,5 +0,0 @@
static box Optimizer {
// placeholder
main(args) { return 0 }
}

View File

@ -1,5 +0,0 @@
static box AST {
// scaffold for future AST node constructors
main(args) { return 0 }
}

View File

@ -1,5 +0,0 @@
static box Lexer {
// scaffold for future implementation
main(args) { return 0 }
}

View File

@ -1,5 +0,0 @@
static box Parser {
// scaffold for future implementation
main(args) { return 0 }
}

View File

@ -1,4 +0,0 @@
Stage1 tests (scaffold)
Add minimal Ny source samples here. Harness TBD.

View File

@ -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",...}.

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -1,7 +0,0 @@
// child file
box Child {
hello() {
return 1
}
}

View File

@ -1,6 +0,0 @@
// cycle A -> B -> A
using "dep_smoke_cycle_b.nyash"
box A {
id() { return 1 }
}

View File

@ -1,6 +0,0 @@
// cycle B -> A
using "dep_smoke_cycle_a.nyash"
box B {
id() { return 2 }
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -1,14 +0,0 @@
Layer Guard — selfhost/vm
Scope and responsibility
- Minimal Ny-based executors and helpers for selfhosting 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 specneutral; new behavior is gated by new tests.

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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())
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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\":[]}"
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}