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 */