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