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 }