Thread overview
Decoding Pattern to a Tuple
Feb 19, 2016
Nordlöw
Feb 19, 2016
Ali Çehreli
Feb 19, 2016
Ali Çehreli
Feb 22, 2016
Nordlöw
Feb 22, 2016
Artur Skawina
February 19, 2016
Have anybody put together a generalised form of findSplit that can split and decode using a compile time parameters somewhat like

"(1)-(2.0)".decode!("(", int, ")", char, "(", double, ")")

evaluates to

to a

tuple!(int, char, double)

with value

tuple(1, '-', 2.0)
February 19, 2016
On 02/19/2016 10:10 AM, Nordlöw wrote:
> Have anybody put together a generalised form of findSplit that can split
> and decode using a compile time parameters somewhat like
>
> "(1)-(2.0)".decode!("(", int, ")", char, "(", double, ")")
>
> evaluates to
>
> to a
>
> tuple!(int, char, double)
>
> with value
>
> tuple(1, '-', 2.0)

The following toy program works with that particular case but can be templatized:

import std.stdio;
import std.string;
import std.regex;
import std.typecons;
import std.conv;

auto decode(string s) {
    // Warning: Treats "012" as int (value 12), not octal (value 10).
    enum intClass = `[0-9]+`;

    enum charClass = `.`;

    // Found on the internet:
    enum floatClass = `[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?`;

    enum expr = format(`[(](%s)[)](%s)[(](%s)[)]`,
                       intClass, charClass, floatClass);
    enum r = ctRegex!expr;

    auto matched = s.match(r);

    if (matched) {
        foreach (e; matched) {
            // We are ignoring potential other matches on the same line and
            // returning just the first match. (Of course, no loop is needed.)
            return tuple(e[1].to!int, e[2].to!char, e[3].to!double);
        }
    }

    return Tuple!(int, char, double)();
}

void main() {
    auto t = decode("(1)-(2.5)");
    writeln(t);
}

Ali

February 19, 2016
On 02/19/2016 11:04 AM, Ali Çehreli wrote:

> can be templatized:

Not ready for prime time but now it's templatized:

import std.stdio;
import std.string;
import std.regex;
import std.typecons;
import std.conv;
import std.algorithm;
import std.range;

template regexClass(T) {
    static if (is (T == int)) {
        // Warning: Treats "012" as int (value 12), not octal (value 10).
        enum regexClass = `[0-9]+`;

    } else static if (is (T == char)) {
        enum regexClass = `.`;

    } else static if (is (T == double)) {
        enum regexClass = `[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?`;

    } else {
        static assert(false, format("Unsupported type %s", arg));
    }
}

string regexEscape(string s) {
    // TODO: Expand the array and fix the logic.
    enum specialRegexChars = [ '(', ')' ];

    return s.map!(c => (specialRegexChars.canFind(c)
                        ? format("[%s]", c)
                        : format("%s", c)))
        .joiner
        .text;
}

auto parseDecodeArgs(Args...)(string matchedElementName) {
    string regexString;
    string tupleString = "return tuple(";

    size_t selectionId = 1;

    foreach (arg; Args) {
        static if (is (arg)) {
            regexString ~= format("(%s)", regexClass!arg);
            tupleString ~=
                format("%s[%s].to!%s, ",
                       matchedElementName, selectionId, arg.stringof);
            ++selectionId;

        } else static if (is (typeof(arg) == string)) {
            regexString ~= regexEscape(arg);

        } else {
            static assert(false, format("Unsupported type %s", typeof(arg)));
        }
    }

    tupleString ~= ");";
    return tuple(regexString, tupleString);
}

auto decode(Args...)(string s) {
    enum parseResult = parseDecodeArgs!Args("e");
    enum r = ctRegex!(parseResult[0]);

    // pragma(msg, parseResult[0]);
    // pragma(msg, parseResult[1]);

    auto matched = s.match(r);

    if (matched) {
        foreach (e; matched) {
            mixin (parseResult[1]);
        }
    }

    return typeof(return)();
}

void main() {
    auto t = decode!("(", int, ")", char, "(", double, ")")("(1)-(2.5)");
    writeln(t);

    // Create a decoder for repeated use
    auto decoder = (string s) => decode!(int, "/", double)(s);

    // Decode each with the same decoder
    auto decoded = ["1/1.5", "2/2.5", "3/3.5"]
                   .map!decoder;

    writeln(decoded);
}

Ali

February 22, 2016
On Friday, 19 February 2016 at 22:16:10 UTC, Ali Çehreli wrote:
> On 02/19/2016 11:04 AM, Ali Çehreli wrote:
>
> > can be templatized:
>
> Not ready for prime time but now it's templatized:

Thanks!
February 22, 2016
On 02/19/16 19:10, Nordlöw via Digitalmars-d-learn wrote:
> Have anybody put together a generalised form of findSplit that can split and decode using a compile time parameters somewhat like
> 
> "(1)-(2.0)".decode!("(", int, ")", char, "(", double, ")")
> 
> evaluates to
> 
> to a
> 
> tuple!(int, char, double)
> 
> with value
> 
> tuple(1, '-', 2.0)

In practice, that isn't necessarily a good idea, because this kind of project-local helpers add a level of obfuscation. But as the language is missing /real/ pattern-matching, this functionality is reinvented again and again. Here's a simple version that takes a single pattern string with the individual patterns placed between "{%" and "%}", and that doesn't support `char` directly (char can be easily gotten from the string).

   template decode(string P, alias S) {
      alias T(A...) = A;
      static if (P.length) {
         import std.algorithm, std.conv;
         enum PS = findSplit(P, `{%`);
         static assert (PS[0]==S[0..PS[0].length]);
         enum PE = findSplit(PS[2], `%}`);
         enum PP = findSplit(PE[2], "{%")[0];
         enum SS = findSplit(S[PS[0].length..$], PP);
         alias decode = T!(mixin(`to!(`~PE[0]~`)(SS[0])`), decode!(PE[2][PP.length..$], SS[2]));
      }
      else
         alias decode = T!();
   }

   enum a = decode!("({%int%}){%string%}({%double%})", "(1)-(2.0)");
   pragma(msg, typeof(a));
   pragma(msg, a);


Just a POC hack; don't use as-is; does not support user defined types (it would have to be a mixin to be able to do that).

artur