1 /**
2  * ae.utils.meta.chain
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 /// Chains are a concept a bit like ranges,
15 /// but which operate on heterogeneous types
16 /// (e.g. a tuple of values).
17 
18 /// Composition is done by a chain of functors.
19 /// To allow state, each functor can be represented
20 /// as a struct.
21 
22 /// Functors return a bool (true if iteration should
23 /// stop, false if it should continue).
24 
25 deprecated("Use ae.utils.meta.tuplerange")
26 module ae.utils.meta.chain;
27 
28 import ae.utils.meta.caps;
29 
30 ///
31 static if (haveAliasStructBinding)
32 unittest
33 {
34 	int a = 2;
35 	int x;
36 	chainIterator(chainFilter!(n => n > a)((int n) { x = n; return true; } ))(1, 2, 3);
37 	assert(x == 3);
38 }
39 
40 static if (haveAliasStructBinding)
41 unittest
42 {
43 	static struct X
44 	{
45 		bool fun(int x)
46 		{
47 			return chainIterator(chainFunctor!(n => n == x))(this.tupleof);
48 		}
49 
50 		long a;
51 		int b;
52 		ubyte c;
53 	}
54 
55 	X x;
56 	assert(!x.fun(5));
57 	x.c = 5;
58 	assert(x.fun(5));
59 }
60 
61 /// Starts the chain by iterating over a tuple.
62 struct ChainIterator(Next)
63 {
64 	Next next; ///
65 
66 	this(ref Next next)
67 	{
68 		this.next = next;
69 	} ///
70 
71 	bool opCall(Args...)(auto ref Args args)
72 	{
73 		foreach (ref arg; args)
74 			if (next(arg))
75 				return true;
76 		return false;
77 	} ///
78 }
79 auto chainIterator(Next)(Next next)
80 {
81 	return ChainIterator!Next(next);
82 } /// ditto
83 
84 unittest
85 {
86 	struct S
87 	{
88 		int a = 1;
89 		long b = 2;
90 		ubyte c = 3;
91 	}
92 	S s;
93 
94 	int[] results;
95 	chainIterator((long n) { results ~= cast(int)n; return  false; })(s.tupleof);
96 	assert(results == [1, 2, 3]);
97 }
98 
99 /// Wraps a function template into a concrete value type functor.
100 struct ChainFunctor(alias fun)
101 {
102 	auto opCall(Arg)(auto ref Arg arg)
103 	{
104 		return fun(arg);
105 	} ///
106 }
107 auto chainFunctor(alias fun)()
108 {
109 	ChainFunctor!fun s;
110 	return s;
111 } /// ditto
112 
113 ///
114 static if (haveAliasStructBinding)
115 unittest
116 {
117 	int[] results;
118 	auto fn = chainFunctor!(n => results ~= cast(int)n);
119 	fn(1);
120 	fn(long(2));
121 	fn(ubyte(3));
122 	assert(results == [1, 2, 3]);
123 }
124 
125 /// Calls next only if pred(value) is true.
126 struct ChainFilter(alias pred, Next)
127 {
128 	Next next; ///
129 
130 	this(Next next) { this.next = next; } ///
131 
132 	bool opCall(T)(auto ref T v)
133 	{
134 		if (pred(v))
135 			return next(v);
136 		return false;
137 	} ///
138 }
139 template chainFilter(alias pred)
140 {
141 	auto chainFilter(Next)(Next next)
142 	{
143 		return ChainFilter!(pred, Next)(next);
144 	}
145 } /// ditto
146 
147 /// Iteration control.
148 struct ChainControl(bool result, Next)
149 {
150 	Next next; ///
151 
152 	this(Next next) { this.next = next; } ///
153 
154 	bool opCall(T)(auto ref T v)
155 	{
156 		cast(void)next(v);
157 		return result;
158 	} ///
159 }
160 template chainControl(bool result)
161 {
162 	auto chainControl(Next)(Next next)
163 	{
164 		return ChainControl!(result, Next)(next);
165 	}
166 } ///
167 alias chainAll = chainControl!false; /// Always continue iteration
168 alias chainFirst = chainControl!true; /// Stop iteration after this element
169 
170 ///
171 static if (haveAliasStructBinding)
172 unittest
173 {
174 	int a = 2;
175 	int b = 3;
176 	int[] results;
177 	foreach (i; 0..10)
178 		chainFilter!(n => n % a == 0)(
179 			chainFilter!(n => n % b == 0)(
180 				(int n) { results ~= n; return false; }))(i);
181 	assert(results == [0, 6]);
182 }
183 
184 /// Calls next with pred(value).
185 struct ChainMap(alias pred, Next)
186 {
187 	Next next; ///
188 
189 	this(Next next) { this.next = next; } ///
190 
191 	bool opCall(T)(auto ref T v)
192 	{
193 		return next(pred(v));
194 	} ///
195 }
196 template chainMap(alias pred)
197 {
198 	auto chainMap(Next)(Next next)
199 	{
200 		return ChainMap!(pred, Next)(next);
201 	}
202 } /// ditto
203 
204 ///
205 unittest
206 {
207 	int result;
208 	chainMap!(n => n+1)(chainMap!(n => n * 2)((int n) { result = n; return false; }))(2);
209 	assert(result == 6);
210 }