1 /** Decoding Algorithms. 2 See_Also: https://forum.dlang.org/post/na7ov2$1np5$1@digitalmars.com 3 */ 4 module nxt.decoding; 5 6 import std.stdio; 7 import std..string; 8 import std.regex; 9 import std.typecons; 10 import std.conv; 11 import std.algorithm; 12 import std.range; 13 14 @safe: 15 16 template regexClass(T) 17 { 18 static if (is(T == int)) 19 { 20 // Warning: Treats "012" as int (value 12), not octal (value 10). 21 enum regexClass = `[0-9]+`; 22 23 } 24 else static if (is(T == char)) 25 { 26 enum regexClass = `.`; 27 28 } 29 else static if (is(T == double)) 30 { 31 enum regexClass = `[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?`; 32 33 } 34 else 35 { 36 static assert(0, format("Unsupported type %s", arg)); 37 } 38 } 39 40 string regexEscape(string s) 41 { 42 // TODO: Expand the array and fix the logic. 43 enum specialRegexChars = ['(', ')']; 44 45 return s.map!(c => (specialRegexChars.canFind(c) ? format("[%s]", c) : format("%s", 46 c))).joiner.text; 47 } 48 49 auto parseDecodeArgs(Args...)(string matchedElementName) 50 { 51 string regexString; 52 string tupleString = "return tuple("; 53 54 size_t selectionId = 1; 55 56 foreach (arg; Args) 57 { 58 static if (is(arg)) 59 { 60 regexString ~= format("(%s)", regexClass!arg); 61 tupleString ~= format("%s[%s].to!%s, ", matchedElementName, selectionId, 62 arg.stringof); 63 ++selectionId; 64 65 } 66 else static if (is(typeof(arg) == string)) 67 { 68 regexString ~= regexEscape(arg); 69 70 } 71 else 72 { 73 static assert(0, format("Unsupported type %s", typeof(arg))); 74 } 75 } 76 77 tupleString ~= ");"; 78 return tuple(regexString, tupleString); 79 } 80 81 auto decode(Args...)(string s) 82 { 83 enum parseResult = parseDecodeArgs!Args("e"); 84 enum r = ctRegex!(parseResult[0]); 85 86 // pragma(msg, parseResult[0]); 87 // pragma(msg, parseResult[1]); 88 89 auto matched = s.match(r); 90 91 if (matched) 92 { 93 foreach (e; matched) 94 { 95 mixin(parseResult[1]); 96 } 97 } 98 99 return typeof(return)(); 100 } 101 102 unittest 103 { 104 auto t = decode!("(", int, ")", char, "(", double, ")")("(1)-(2.5)"); 105 static assert(is(typeof(t) == Tuple!(int, char, double))); 106 assert(t == Tuple!(int, char, double)(1, '-', 2.5)); 107 108 // Create a decoder for repeated use 109 auto decoder = (string s) => decode!(int, "/", double)(s); 110 111 // Decode each with the same decoder 112 auto decoded = ["1/1.5", "2/2.5", "3/3.5"].map!decoder; 113 114 // writeln(decoded); 115 }