selfhost/tools: add dep_tree_simple.nyash (single-box functional) and run dep_tree.sh with interpreter + plugin builtins; groundwork for include+using/module JSON deps

This commit is contained in:
Selfhosting Dev
2025-09-07 20:59:18 +09:00
parent caf0e922ef
commit 1069f558a5
2 changed files with 233 additions and 2 deletions

View File

@ -0,0 +1,231 @@
// 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) {
local out = new ArrayBox()
local i = 0
local n = src.length()
local start = 0
loop(true) {
if i == n { out.push(src.substring(start, i)) return out }
local ch = src.substring(i, i+1)
if ch == "\n" { out.push(src.substring(start, i)) start = i + 1 }
i = i + 1
}
return out
}
// ---- scanners ----
scan_includes(src) {
local out = new ArrayBox()
if src == null { return out }
local lines = me.split_lines(src)
local i = 0
loop(i < lines.length()) {
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)) }
}
i = i + 1
}
return out
}
scan_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 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)
}
i = i + 1
}
return out
}
scan_modules(src) {
local out = new ArrayBox()
if src == null { return out }
local lines = me.split_lines(src)
local i = 0
loop(i < lines.length()) {
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)
}
}
i = i + 1
}
return out
}
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 i = 0
loop(i < paths.length()) {
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 incs = me.scan_includes(src)
local uses = me.scan_usings(src)
local mods = me.scan_modules(src)
out.set("includes", incs)
out.set("uses", uses)
out.set("modules", mods)
local mod_map = new MapBox()
local mi = 0
loop(mi < mods.length()) {
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.length()) {
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.length()) {
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
}
}