summaryrefslogtreecommitdiff
path: root/appl/cmd/tsort.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/tsort.b')
-rw-r--r--appl/cmd/tsort.b133
1 files changed, 133 insertions, 0 deletions
diff --git a/appl/cmd/tsort.b b/appl/cmd/tsort.b
new file mode 100644
index 00000000..5993fa31
--- /dev/null
+++ b/appl/cmd/tsort.b
@@ -0,0 +1,133 @@
+implement Tsort;
+
+#
+# tsort -- topological sort
+#
+# convert a partial ordering into a linear ordering
+#
+# Copyright © 2004 Vita Nuova Holdings Limited
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+Tsort: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+Item: adt {
+ name: string;
+ mark: int;
+ succ: cyclic list of ref Item; # node's successors
+
+ precede: fn(a: self ref Item, b: ref Item);
+};
+
+Q: adt {
+ item: ref Item;
+ next: cyclic ref Q;
+};
+
+items, itemt: ref Q; # use a Q not a list only to keep input order
+nitem := 0;
+bout: ref Iobuf;
+
+init(nil: ref Draw->Context, nil: list of string)
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+
+ bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
+ input();
+ output();
+ bout.flush();
+}
+
+error(s: string)
+{
+ sys->fprint(sys->fildes(2), "tsort: %s\n", s);
+ raise "fail:error";
+}
+
+input()
+{
+ b := bufio->fopen(sys->fildes(0), Sys->OREAD);
+ while((line := b.gets('\n')) != nil){
+ (nf, fld) := sys->tokenize(line, " \t\n");
+ if(fld != nil){
+ a := finditem(hd fld);
+ while((fld = tl fld) != nil)
+ a.precede(finditem(hd fld));
+ }
+ }
+}
+
+Item.precede(a: self ref Item, b: ref Item)
+{
+ if(a != b){
+ for(l := a.succ; l != nil; l = tl l)
+ if((hd l) == b)
+ return;
+ a.succ = b :: a.succ;
+ }
+}
+
+finditem(s: string): ref Item
+{
+ # would use a hash table for large sets
+ for(il := items; il != nil; il = il.next)
+ if(il.item.name == s)
+ return il.item;
+ i := ref Item;
+ i.name = s;
+ i.mark = 0;
+ if(items != nil)
+ itemt = itemt.next = ref Q(i, nil);
+ else
+ itemt = items = ref Q(i, nil);
+ nitem++;
+ return i;
+}
+
+dep: list of ref Item;
+
+output()
+{
+ for(k := items; k != nil; k = k.next)
+ if((q := k.item).mark == 0)
+ visit(q, nil);
+ for(; dep != nil; dep = tl dep)
+ bout.puts((hd dep).name+"\n");
+}
+
+# visit q's successors depth first
+# parents is only used to print any cycles, and since it matches
+# the stack, the recursion could be eliminated
+visit(q: ref Item, parents: list of ref Item)
+{
+ q.mark = 2;
+ parents = q :: parents;
+ for(sl := q.succ; sl != nil; sl = tl sl)
+ if((s := hd sl).mark == 0)
+ visit(s, parents);
+ else if(s.mark == 2){
+ sys->fprint(sys->fildes(2), "tsort: cycle in input\n");
+ rl: list of ref Item;
+ for(l := parents;; l = tl l){ # reverse to be closer to input order
+ rl = hd l :: rl;
+ if(hd l == s)
+ break;
+ }
+ for(l = rl; l != nil; l = tl l)
+ sys->fprint(sys->fildes(2), "tsort: %s\n", (hd l).name);
+ }
+ q.mark = 1;
+ dep = q :: dep;
+}