On Thursday, 22 January 2015 at 19:13:46 UTC, Meta wrote:
On Thursday, 22 January 2015 at 19:12:32 UTC, zeljkog wrote:
On 22.01.15 20:05, Meta wrote:
On Thursday, 22 January 2015 at 19:00:47 UTC, zeljkog wrote:
On 22.01.15 19:26, Meta wrote:
On Thursday, 22 January 2015 at 18:23:00 UTC, Meta wrote:
Whoops, I forgot to make it a template to force CTFE.


You can force CTFE assigning to manifest constant.

enum t = charRange!...

By wrapping it in a template the array will always be generated at
compile time, even if you assign it to a runtime variable.

Yes, but then you can not use runtime spec.

OP wanted it to be done at compile time.

A small improvement to ensure the input consists of all ASCII characters, and support for backward intervals.

import std.stdio;

template charRange(string spec)
{
    static processSpec(string spec)
    {
        import std.algorithm;
        import std.ascii;
        import std.conv;
        import std.range;
        import std.string;

        return spec.split(',').map!((s)
        {
            s = s.strip;
            assert(s.all!(c => cast(ulong)c < 256),
"Expected all characters in 'spec' to be ASCII");

            auto start = cast(ubyte)s[1..$].front;
            auto end = cast(ubyte)s[0..$-1].back;

            return start <= end
                ? iota(start, end + 1)
                    .map!(c => cast(char)c).array
                : iota(end, start + 1)
                    .retro
                    .map!(c => cast(char)c).array;
        }).join;
    }

    enum charRange = processSpec(spec);
}

unittest
{
assert(charRange!q{ 'a'..'z', 'A'..'Z', '0'..'9' } == charRange!q{ '9'..'0', 'Z'..'A', 'z'..'a' }.reverse);
}

void main(string[] argv)
{
    auto t1 = charRange!q{ 'a'..'z', 'A'..'Z', '0'..'9' };
    writeln(t1);

    auto t2 = charRange!q{ '9'..'0', 'Z'..'A', 'z'..'a' };
    writeln(t2);
}

Reply via email to