1 module tooling.TreeRange;
2 
3 import std.array, std.algorithm, std.range;
4 
5 // Walk a nested (tree) structure "depth-first". Return the path from root to
6 // leaf in each step.
7 
8 auto treeRange(alias isBranch, alias children, T)(T[] root)
9 {
10   struct Walker
11   {
12 	this(T[] nodes)
13 	{
14 	  nodes_ = nodes;
15 	}
16 	bool empty() { return nodes_.empty && stack_.empty; }
17 	T[] front() { return stack_.filter!(e => !e.empty).map!(e => e.front).array ~ nodes_.front; }
18 	Walker save() { return this; }
19 	void popFront()
20 	{
21 	  if (!nodes_.empty)
22 	  {
23 		auto f = nodes_.front;
24 		if (isBranch(f))
25 		{
26 		  stack_ ~= nodes_;
27 		  nodes_ = children(f);
28 		}
29 		else
30 		{
31 		  nodes_ = nodes_[1 .. $];
32 		}
33 	  }
34 	  while (nodes_.empty && !stack_.empty)
35 	  {
36 		nodes_ = stack_.back()[1 .. $];
37 		stack_.popBack();
38 	  }
39 	}
40 
41 	T[] nodes_;
42 	T[][] stack_;
43   }
44 
45   return Walker(root);
46 }
47 
48 unittest
49 {
50   import std.stdio, std.conv;
51   class Node
52   {
53 	this(int payload, Node[] children) {
54 	  this.payload = payload;
55 	  this.children = children;
56 	}
57 	int payload;
58 	Node[] children;
59   }
60   auto nodes = [new Node(1, [new Node(2, [new Node(3, [])]),
61 							 new Node(4, [new Node(5, [new Node(6, [])])]),
62 							 new Node(7, [])
63 							 ])
64 				];
65 
66   auto r = treeRange!(t => !t.children.empty, t => t.children)(nodes);
67   foreach (e; r) {
68 	writeln(e.map!(i => to!string(i.payload)).joiner("-"));
69   }
70 }