diff --git a/apps/selfhost/tools/dep_tree_simple.nyash b/apps/selfhost/tools/dep_tree_simple.nyash new file mode 100644 index 00000000..ca477b4c --- /dev/null +++ b/apps/selfhost/tools/dep_tree_simple.nyash @@ -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 + } +} diff --git a/tools/dep_tree.sh b/tools/dep_tree.sh index 31f8b5f9..ecc2dad8 100644 --- a/tools/dep_tree.sh +++ b/tools/dep_tree.sh @@ -3,6 +3,6 @@ set -euo pipefail ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) ENTRY=${1:-apps/selfhost/ny-parser-nyash/main.nyash} -NYASH_DISABLE_PLUGINS=0 NYASH_CLI_VERBOSE=0 \ - "$ROOT_DIR/target/release/nyash" --backend vm \ +NYASH_DISABLE_PLUGINS=0 NYASH_CLI_VERBOSE=0 NYASH_USE_PLUGIN_BUILTINS=1 \ + "$ROOT_DIR/target/release/nyash" --backend interpreter \ "$ROOT_DIR/apps/selfhost/tools/dep_tree_main.nyash" <<<"$ENTRY"