1 module versioning;
2 /*
3  * ========================== BOOST LICENCE ===========================
4  * |             http://www.boost.org/LICENSE_1_0.txt                 |
5  * ====================================================================
6  * Do whatever you want man, I don't care. Just don't blame me when
7  * you screw up. And oh boy, you're going to want to be careful with
8  * this module.
9  * ====================================================================
10  *                            ^
11  *  ~~ Versioning ~~                            ^
12  *                                                            ^
13  *  Logical operations for version and debug tags in the D Language        
14  *
15  *         ^                                         by Dmetri Wolf
16  *                        ^                                        ^
17  *                           ^    ^               ^
18  *                   Ooo   
19  *                       Oo                      sunset sunset
20  *    ^                  __||_____           sunset splash sunset
21  * ~~~~~~~~~~~~~~~~~~~~~ \_______/ ~~~~~~~~sunset in the background~~
22  *   ~~             ~~ ~~            ~            ~~ ~       ~~       ~~
23  * --------------------------------------------------------------------
24  * | Meanwhile, in another tab... | 
25  * --------------------------------
26  * You've discovered that Walter doesn't want you to use boolean 
27  * operations in your versioning. Maybe he's right, but we gotta 
28  * make our own mistakes. ;)
29  * ---
30  * This module was created to make debugging a little less annoying.
31  * Don't get me wrong, debugging still sucks, but at least you can
32  * be pretty specific about which version of your output you're going
33  * to get and when. Just don't cock it up or someone will say "I told
34  * you so."
35  *
36  * You have to fill in some mixins at module scope, but then you can
37  * put a nice debug(mouseANDkeyboard) next to the debug output of your
38  * ctrl+click code.
39  *
40  * --------- Features all your favourite boolean operators! ----------
41  * - OR: creates a logical OR version between two versions 
42  *     (either version turns on OR versions)
43  * - AND: creates a logical AND version between two versions
44  *     (only both versions on will turn on this version)
45  * - XOR: creates a logical OR version between two versions
46  *     (one or the other version will switch on this version, but not both)
47  * - NOT: creates an opposite version of the provided version
48  *     (Literally just NOTversion)
49  * - ManyAND: combines multiple versions into a new, defined version
50  *     (ANDs as many versions as you like, but you provide new name)
51  * PLUS OTHER USEFUL THINGS SUCH AS:
52  * - Umbrella: creates an umbrella term out of several versions
53  *     (all child versions will switch on when umbrella term is used)
54  * - Base: creates a base version for a group of versions
55  *     (if any of the child versions switches on it will be switched on)
56  *
57  * ---------- IMPORTANT NOTICES -----------
58  * -- Feeding is UNDEFINED --
59  * There aren't any guarentees feeding one result into another will
60  * do what you expect, eg "mixin(XOR("NOTvers1","vers2ORvers3"));" may or
61  * may not work as you expect. I expect that case will work if you've already
62  * produced NOTvers1 and vers2ORvers3, but if you start throwning Umbrellas
63  * in there the order you have to write it all in might not be very clear.
64  * Feeding will also produce lots of very ugly versions, so, taking the example,
65  * you could be using version(NOTvers1XORvers2ORvers3) which would be...
66  * ...you have eyes, you can see why that's not so great.
67  *
68  * -- You can use the new versions in any order! --
69  * mixin( Version( "sandwich", "AND", "pizza" ) ); will result in both
70  * sandwichANDpizza and pizzaANDsandwich so you don't have to remember
71  * which order you defined them as. 
72  *
73  * -- !!! ORDER OF USE !!! --------------------------------------------------+
74  * It is recommended that you use Umbrella before using the other finctons   |
75  * as Umbrella may need to switch on versions to be used by those functions. |
76  * If you are feeding(see above), then you need to determine those BEFORE    |
77  * you feed them in.                                                         |
78  * --------------------------------------------------------------------------+
79  * --- Example --
80 	module animals;
81 
82 	import std.stdio;
83 	import versioning;
84 
85 	// Base will turn on if any of its children are present 
86 	// but it will not turn on its children
87 	// use it in your code when any of its children could satisfy a condition
88 	mixin( VersionBase( "mammal", "dog", "cat", "mouse" ) ); // mammal
89 
90 	// Umbrella will only be active if turned on explicitly a la -version=animal
91 	// all of its children will then be active, usually you won't use an Umbrella
92 	// in your main code, it just works as a master switch to turn on other things.
93 	mixin( VersionUmbrella( "animal", "dog", "cat", "mouse", "frog" ) ); // animal
94 
95 	// Be careful not to confuse the above two.
96 
97 	mixin( VersionOR( "dog", "cat" ) ); // dogORcat
98 	mixin( VersionAND( "dog", "cat" ) ); // dogANDcat
99 	mixin( VersionNOT( "mouse" ) ); // NOTmouse
100 	mixin( VersionXOR( "cat", "mouse" ) ); // catXORmouse
101 	mixin( VersionNOT( "animal" ) ); // NOTanimal
102 	mixin( VersionNOT( "mammal" ) ); // NOTmammal
103 	mixin( VersionAND( "NOTmammal", "frog" ) ); // NOTmammalANDfrog
104 	
105 	//These will do whatevs
106 	mixin( Debug( "u", "alphabet", "a", "k" ) ); //umbrella named alphabet
107 	mixin( Version( "b", "alpha", "a", "k" ) ); //base named alpha
108 
109 	mixin( Version( "sandwich", "XOR", "pizza" ) ); // sandwichXORpizza
110 	                    							//you're welcome.
111 	void main(){
112 	    version(dogORcat) writeln("*something rustles in the bushes*");
113 	    version(catANDdog) writeln("Woof woof, meow!"); //order of version doesn't matter
114 	    version(mouseXORcat) writeln("Your pet doesn't seem concerned with anything.");
115 	    version(NOTmouse) writeln("You have so much cheese right now.");
116 		version(NOTanimal) writeln("There could be animals here, idk...");
117 		version(mammal) writeln("Certainly there's something warm blooded here!");
118 		version(NOTmammal) writeln("Welp, at least there's no hair on the carpet");
119 		version(frogANDNOTmammal) writeln("It's just you and me, my slimy little friend...");
120 		version(NOTdog) // won't be called as it's not defined anywhere
121 	}
122  * --- /Example ---
123  *
124  * As you can see, you have to place a mixin at the module scope each time you want to use
125  * one of these operations. It's tedious. Don't blame me, talk to Walter about it. Maybe     
126  * he's doing you a favour, making it too damn annoying to lazily screw up your versions.
127  *
128  * I haven't run any tests on how much impact creating many compile-time mixins/statements
129  * is going to have on how fast you can compile.
130  */
131 
132 import std.format, core.vararg;
133 
134 // Pretty self explanitory. 
135 // Change these to change global Grammar rules.
136 enum Grammar : string {
137 	SeparatorOR = "OR",
138 	SeparatorAND = "AND",
139 	SeparatorXOR = "XOR",
140 	PrefixNOT = "NOT",
141 	SuffixNOT = "",
142 	Prefix = "", //Global prefix -does not override
143 	Suffix = "", // Global suffix -does not override
144 	PrefixOR = "",
145 	SuffixOR = "",
146 	PrefixXOR = "",
147 	SuffixXOR = "",
148 	PrefixAND = "",
149 	SuffixAND = "",
150 	KeyUmbrella = "u",
151 	KeyBase = "b"
152 }
153 
154 //Version section
155 string Version( string[] arguments... ){
156 // Easy function to call each of the others with readable syntax:
157 // mixin(Version("dog","OR","cat"));
158 // ManyAND works with // mixin(Version("AND","ANDermals","dog","cat","mouse")); // ANDermals
159 // Umberella are called with "u" and "b respectively
160 // Just be careful that you use the right grammar
161 // You can't use on the fly grammar or write in suffixes etc
162 	if( arguments[0] == Grammar.PrefixNOT ){
163 		return VersionNOT(arguments[1]);
164 	} else if( arguments[1] == Grammar.SeparatorOR ){
165 		return VersionOR(arguments[0],arguments[2]);
166 	} else if( arguments[1] == Grammar.SeparatorXOR ){
167 		return VersionXOR(arguments[0],arguments[2]);
168 	} else if( arguments[1] == Grammar.SeparatorAND ){
169 		return VersionAND(arguments[0],arguments[2]);
170 	} else if( arguments[0] == Grammar.KeyUmbrella ){
171 		return VersionUmbrella( arguments[1], arguments[2..$] );
172 	} else if( arguments[0] == Grammar.KeyBase ){
173 		return VersionBase( arguments[1], arguments[2..$] );
174 	} else if( arguments[0] == Grammar.SeparatorAND ){
175 		return VersionManyAND( arguments[1], arguments[2..$] );
176 	}
177 	assert(0, "Versioning Version(string...) was use incorrectly.");
178 }
179 
180 string VersionUmbrella( string umbrella_term, string[] subsidiaries... ){
181 	return Umbrella( "version", umbrella_term, subsidiaries );
182 }
183 string VersionBase( string base_term, string[] derivatives... ){
184 	return Base( "version", base_term, derivatives );
185 }
186 string VersionManyAND( string new_name, string[] subsidiaries... ){
187 	return ManyAND( "version", new_name, subsidiaries );
188 }
189 string VersionOR( string version1, string version2, string separator = "", 
190   string prefix = "", string suffix = "" ){
191 	return OR( "version", version1, version2, separator, prefix, suffix );
192 }
193 string VersionAND( string version1, string version2, string separator = "", 
194   string prefix = "", string suffix = "" ){
195 	return AND( "version", version1, version2, separator, prefix, suffix );
196 }
197 string VersionXOR( string version1, string version2, string separator = "", 
198   string prefix = "", string suffix = "" ){
199 	return XOR( "version", version1, version2, separator, prefix, suffix );
200 }
201 string VersionNOT( string version1, string prefix = "", string suffix = "" ){
202 	return NOT( "version", version1, prefix, suffix );
203 }
204 //Debug section
205 string Debug( string[] arguments... ){
206 	if( arguments[0] == Grammar.PrefixNOT ){
207 		return DebugNOT(arguments[1]);
208 	} else if( arguments[1] == Grammar.SeparatorOR ){
209 		return DebugOR(arguments[0],arguments[2]);
210 	} else if( arguments[1] == Grammar.SeparatorXOR ){
211 		return DebugXOR(arguments[0],arguments[2]);
212 	} else if( arguments[1] == Grammar.SeparatorAND ){
213 		return DebugAND(arguments[0],arguments[2]);
214 	} else if( arguments[0] == Grammar.KeyUmbrella ){
215 		return DebugUmbrella( arguments[1], arguments[2..$] );
216 	} else if( arguments[0] == Grammar.KeyBase ){
217 		return DebugBase( arguments[1], arguments[2..$] );
218 	} else if( arguments[0] == Grammar.SeparatorAND ){
219 		return DebugManyAND( arguments[1], arguments[2..$] );
220 	}
221 	assert(0, "Versioning Version(string...) was use incorrectly.");
222 }
223 string DebugUmbrella( string umbrella_term, string[] subsidiaries... ){
224 	return Umbrella( "debug", umbrella_term, subsidiaries );
225 }
226 string DebugBase( string base_term, string[] derivatives... ){
227 	return Base( "version", base_term, derivatives );
228 }
229 string DebugManyAND( string new_name, string[] subsidiaries... ){
230 	return ManyAND( "debug", new_name, subsidiaries );
231 }
232 string DebugOR( string version1, string version2, string separator = "", 
233   string prefix = "", string suffix = "" ){
234 	return OR( "debug", version1, version2, separator, prefix, suffix );
235 }
236 string DebugAND( string version1, string version2, string separator = "", 
237   string prefix = "", string suffix = "" ){
238 	return AND( "debug", version1, version2, separator, prefix, suffix );
239 }
240 string DebugXOR( string version1, string version2, string separator = "", 
241   string prefix = "", string suffix = "" ){
242 	return XOR( "debug", version1, version2, separator, prefix, suffix );
243 }
244 string DebugNOT( string version1, string prefix = "NOT", string suffix = "" ){
245 	return NOT( "debug", version1, prefix, suffix );
246 }
247 /* String constructors for each type of operation, independant of keyword used. */
248 private:
249 string Umbrella( string term, string umbrella_term,
250   string[] subsidiaries...){
251 	string all = format( "%s(%s){\n", term, umbrella_term );
252 	foreach( sub; subsidiaries ){
253 		all = format( "%s%s = %s;\n", all, term, sub );
254 	}
255 	all = format( "%s}", all );
256 	return all;
257 }
258 string Base( string term, string base_term,
259   string[] derivatives...){
260 	string all;
261 	foreach( sub; derivatives ){
262 		all = format( "%s%s(%s) %s = %s;\n", all, term, sub, term, base_term );
263 	}
264 	return all;
265 }
266 string OR( string term, string version1, string version2, 
267   string separator = "", string prefix = "", string suffix = "" ){
268   	string s = separator;
269   	if( separator == "" ){
270 		s = Grammar.SeparatorOR;
271 	}
272 	string p = prefix;
273 	if( prefix == "" ){
274 		p = Grammar.PrefixOR;
275 		if( p == "" ){
276 			p = Grammar.Prefix;
277 		}
278 	}
279 	string su = suffix;
280 	if( suffix == "" ){
281 		su = Grammar.SuffixOR;
282 		if( su == "" ){
283 			su= Grammar.Suffix;
284 		}
285 	}
286 	return format( "%s(%s) %s = %s%s%s%s%s;\n%s(%s) %s = %s%s%s%s%s;
287 %s(%s) %s = %s%s%s%s%s;\n%s(%s) %s = %s%s%s%s%s;", 
288 	  term, version1, term, p, version1, s, version2, su, term, version2, term, 
289 	  p, version1, s, version2, su, term, version1, term, p, version2, s, 
290 	  version1, su, term, version2, term, p, version2, s, version1, su );
291 }
292 string AND( string term, string version1, string version2, 
293   string separator = "", string prefix = "", string suffix = "" ){
294 	string s = separator;
295   	if( separator == "" ){
296 		s = Grammar.SeparatorAND;
297 	}
298 	string p = prefix;
299 	if( prefix == "" ){
300 		p = Grammar.PrefixAND;
301 		if( p == "" ){
302 			p = Grammar.Prefix;
303 		}
304 	}
305 	string su = suffix;
306 	if( suffix == "" ){
307 		su = Grammar.SuffixAND;
308 		if( su == "" ){
309 			su= Grammar.Suffix;
310 		}
311 	}
312 	return format( "%s(%s)%s(%s) %s = %s%s%s%s%s;\n%s(%s)%s(%s) %s = %s%s%s%s%s;", term, version1, 
313 	 term, version2, term, p, version1,
314 	  s, version2, su, term, version1, term, version2, term, p, version2, 
315 	  s, version1, su );
316 }
317 string ManyAND( string term, string name, string[] versions... ){
318 	string s;
319 	import std.stdio;
320 	foreach( vers; versions[] ){
321 		s = format( "%s%s(%s)", s, term, vers );
322 	}
323 	s = format( "%s %s = %s;", s, term, name );
324 	return s;
325 }
326 string XOR( string term, string version1, string version2, 
327   string separator = "", string prefix = "", string suffix = "" ){
328   	string s = separator;
329   	if( separator == "" ){
330 		s = Grammar.SeparatorXOR;
331 	}
332 	string p = prefix;
333 	if( prefix == "" ){
334 		p = Grammar.PrefixXOR;
335 		if( p == "" ){
336 			p = Grammar.Prefix;
337 		}
338 	}
339 	string su = suffix;
340 	if( suffix == "" ){
341 		su = Grammar.SuffixXOR;
342 		if( su == "" ){
343 			su= Grammar.Suffix;
344 		}
345 	}
346 
347 	return format( "%s(%s){\n%s(%s){\n} else {\n%s = %s%s%s%s%s;\n%s = %s%s%s%s%s;\n}
348 } else {\n%s(%s){\n%s = %s%s%s%s%s;\n%s = %s%s%s%s%s;\n}\n}", 
349 	  term, version1, term, version2, term, p, version1, s, version2, su, term, p, version2, s, version1, su,
350 	  term, version2, term, p, version1, s, version2, su, term, p, version2, s, version1, su );
351 }
352 string NOT( string term, string version1, string prefix = "", string suffix = "" ){
353 	string p = prefix;
354 	if( prefix == "" ){
355 		p = Grammar.PrefixXOR;
356 		if( p == "" ){
357 			p = Grammar.Prefix;
358 		}
359 	}
360 	string su = suffix;
361 	if( suffix == "" ){
362 		su = Grammar.SuffixXOR;
363 		if( su == "" ){
364 			su= Grammar.Suffix;
365 		}
366 	}
367 	return format( "%s(%s){\n}else{\n%s = %s%s%s; \n}\n", term, version1, term,
368 	  p, version1, su );
369 }
370 
371 unittest {
372 	import std.stdio;
373 	// mixin(OR( "debug", "ver1", "ver2", "_or_" )); //Error at this scope
374 
375 	// As these outputs need to be printed at module level they can't be
376 	// tested as code in a unittest, but they can be printed to the screen.
377 	writeln( "OR Test: \n", OR( "version", "ver1", "ver2", "_or_" ) );
378 	writeln( "AND Test: \n", AND( "debug", "ver1", "ver2", "_and_" ) );
379 	writeln( "XOR Test: \n", XOR( "version", "ver1", "ver2", "_xor_" ) );
380 	writeln( "NOT Test: \n", NOT( "debug", "name", "not_", "_not" ) );
381 
382 	writeln( "Umbrealla Test: \n", Umbrella( "debug", "abc", "a", "b", "c", "d" ) );
383 	writeln( "Base Test: \n", Base( "debug", "food", "cheese", "potato" ) );
384 
385 	writeln( "Debug Test: umbrella \n", Debug( "u", "weather", "rain", "hail", "snow" ) );
386 	writeln( "Version Test: base\n", Version( "b", "weather", "rain", "hail", "snow" ) );
387 	writeln( "Version Test: manyand\n", Version( "AND", "weather", "rain", "hail", "snow" ) );
388 	writeln( "Debug Test: not \n", Debug( "NOT", "weather" ) );
389 	writeln( "Version Test: xor\n", Version( "friend", "XOR", "enemy" ) );
390 	writeln( "Version Test: or\n", Version( "friend", "OR", "enemy" ) );
391 	writeln( "Version Test: and\n", Version( "friend", "AND", "enemy" ) );
392 }
393 /* Test code if unittests worked:
394 
395 	mixin(DebugOR( "ver1", "ver2", "_or_" ) );
396 	mixin(VersionAND( "ver1", "ver2", "_and_" ) );
397 	mixin(VersionXOR( "ver1", "ver2", "_xor_" ) );
398 	mixin(DebugNOT( "name", "not_", "_not" ) );
399 	mixin(DebugUmbrella( "abc", "a", "b", "c", "d" ) );
400 	mixin(VersionBase( "food", "cheese", "potato" ) );
401 	mixin(Debug( "u", "weather", "rain", "hail", "snow" ) );
402 	mixin(Version( "b", "weather", "rain", "hail", "snow" ) );
403 	mixin(Version( "AND", "weather", "rain", "hail", "snow" ) );
404 	mixin(Debug( "NOT", "weather" ) );
405 	mixin(Version( "friend", "XOR", "enemy" ) );
406 	mixin(Version( "friend", "OR", "enemy" ) );
407 	mixin(Version( "friend", "AND", "enemy" ) );
408 
409 */