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