hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

deps.ha (3576B)


      1 // SPDX-License-Identifier: GPL-3.0-only
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use fmt;
      5 use getopt;
      6 use hare::ast;
      7 use hare::module;
      8 use hare::parse;
      9 use os;
     10 use path;
     11 use sort;
     12 use sort::cmp;
     13 
     14 type deps_fmt = enum {
     15 	DOT,
     16 	TERM,
     17 };
     18 
     19 type link = struct {
     20 	depth: uint,
     21 	child: size,
     22 	final: bool,
     23 };
     24 
     25 fn deps(name: str, cmd: *getopt::command) (void | error) = {
     26 	let tags = default_tags();
     27 	defer free(tags);
     28 
     29 	let build_dir: str = "";
     30 	let goal = deps_fmt::TERM;
     31 
     32 	for (let opt .. cmd.opts) {
     33 		switch (opt.0) {
     34 		case 'd' =>
     35 			goal = deps_fmt::DOT;
     36 		case 'T' =>
     37 			merge_tags(&tags, opt.1)?;
     38 		case =>
     39 			abort();
     40 		};
     41 	};
     42 
     43 	if (len(cmd.args) > 1) {
     44 		getopt::printusage(os::stderr, name, cmd.help)!;
     45 		os::exit(os::status::FAILURE);
     46 	};
     47 
     48 	const input = if (len(cmd.args) == 0) os::getcwd() else cmd.args[0];
     49 
     50 	let ctx = module::context {
     51 		harepath = harepath(),
     52 		harecache = harecache(),
     53 		tags = tags,
     54 	};
     55 	let mods: []module::module = [];
     56 
     57 	let mod = match (parse::identstr(input)) {
     58 	case let id: ast::ident =>
     59 		yield id;
     60 	case parse::error =>
     61 		static let buf = path::buffer { ... };
     62 		path::set(&buf, os::realpath(input)?)?;
     63 		yield &buf;
     64 	};
     65 	module::gather(&ctx, &mods, mod)?;
     66 	defer module::free_slice(mods);
     67 
     68 	switch (goal) {
     69 	case deps_fmt::TERM =>
     70 		deps_graph(&mods);
     71 	case deps_fmt::DOT =>
     72 		fmt::println("strict digraph deps {")!;
     73 		for (let mod .. mods) {
     74 			for (let dep .. mod.deps) {
     75 				const child = mods[dep.0];
     76 				fmt::printfln("\t\"{}\" -> \"{}\";",
     77 					mod.name, child.name)!;
     78 			};
     79 		};
     80 		fmt::println("}")!;
     81 	};
     82 };
     83 
     84 fn deps_graph(mods: *[]module::module) void = {
     85 	if (len(mods) == 1 && len(mods[0].deps) == 0) {
     86 		fmt::println(mods[0].name, "has no dependencies")!;
     87 		return;
     88 	};
     89 
     90 	let links: []link = [];
     91 	defer free(links);
     92 	let depth: []uint = alloc([0...], len(mods));
     93 	// traverse in reverse because reverse-topo-sort
     94 	for (let i = len(mods) - 1; i < len(mods); i -= 1) {
     95 		// reverse-sort deps so that we know the last in the list is the
     96 		// "final" child during show_deps
     97 		sort::sort(mods[i].deps, size((size, ast::ident)), &revsort);
     98 
     99 		for (let j = 0z; j < len(links); j += 1) {
    100 			if (i < links[j].child) {
    101 				continue;
    102 			};
    103 			if (depth[i] <= links[j].depth) {
    104 				depth[i] = links[j].depth + 1;
    105 			};
    106 		};
    107 
    108 		// print in-between row
    109 		for (let d = 0u; d < depth[i]; d += 1) {
    110 			let passing = false;
    111 			for (let j = 0z; j < len(links); j += 1) {
    112 				if (i < links[j].child) {
    113 					continue;
    114 				};
    115 				if (d == links[j].depth) {
    116 					passing = true;
    117 				};
    118 			};
    119 			fmt::print(if (passing) "│  " else "   ")!;
    120 		};
    121 		if (i < len(mods) - 1) {
    122 			fmt::println()!;
    123 		};
    124 
    125 		// print row itself
    126 		let on_path = false;
    127 		for (let d = 0u; d < depth[i]; d += 1) {
    128 			let connected = false;
    129 			let passing = false;
    130 			let final = false;
    131 			for (let j = 0z; j < len(links); j += 1) {
    132 				if (i < links[j].child) {
    133 					continue;
    134 				};
    135 				if (d == links[j].depth) {
    136 					passing = true;
    137 					if (i == links[j].child) {
    138 						connected = true;
    139 						on_path = true;
    140 						if (links[j].final) {
    141 							final = true;
    142 						};
    143 					};
    144 				};
    145 			};
    146 			fmt::print(
    147 				if (final) "└──"
    148 				else if (connected) "├──"
    149 				else if (on_path) "───"
    150 				else if (passing) "│  "
    151 				else "   "
    152 			)!;
    153 		};
    154 		fmt::println(mods[i].name)!;
    155 		for (let j = 0z; j < len(mods[i].deps); j += 1) {
    156 			append(links, link{
    157 				depth = depth[i],
    158 				child = mods[i].deps[j].0,
    159 				final = len(mods[i].deps) == j + 1,
    160 			});
    161 		};
    162 	};
    163 };
    164 
    165 // sorts in reverse
    166 fn revsort(a: const *opaque, b: const *opaque) int = -cmp::sizes(a, b);