DIP 50 - AST macros

Simen Kjærås simen.kjaras at gmail.com
Sun Nov 17 11:41:26 PST 2013


On 13.11.2013 20:05, Ellery Newcomer wrote:
> On Wednesday, 13 November 2013 at 10:51:48 UTC, Simen Kjærås wrote:
>> On 12.11.2013 18:53, Ellery Newcomer wrote:
>>
>> It's perfectly possible in D to make this work:
>>
>
> hey, cool impl
>
> *comprehends code*
>
> I mean Ewww
>
>> I *can* make that work. I'm not going to.
>>
>> --
>>   Simen
>
> I concur with the second part.

I decided to abandon sanity. Luckily, only for the named parameters. I 
now have this code working:


void test(int a, int b = 2, string c = "foo", int d = 14) {
}

alias test2 = nameify!test;

void main() {
     test2(1, Args.d = 4, Args.c = "bar");
}

With reasonable error messages for duplicate and missing parameters, as 
well as type mismatches.

Source is attached. I hope God forgives me.

-- 
   Simen
-------------- next part --------------
import std.stdio : writeln;
import std.traits : ParameterTypeTuple, ParameterIdentifierTuple, ParameterDefaultValueTuple;
import std.typetuple : TypeTuple, staticIndexOf, staticMap;
import std.conv : to;

struct NamedArg(string _name, Arg) {
    enum name = _name;
    Arg value;
    alias value this;
}

template isNotNamedArg(T...) if (T.length == 1) {
    enum isNotNamedArg = !isNamedArg!T;
}
template isNamedArg(T...) if (T.length == 1) {
    static if (is(typeof(T[0]))) {
        enum isNamedArg = isNamedArg!(typeof(T[0]));
    } else {
        enum isNamedArg = is(T[0] == NamedArg!U, U...);
    }
} unittest {
    assert( isNamedArg!(NamedArg!("a", int)));
    assert(!isNamedArg!int);
    NamedArg!("a", float) f;
    assert(isNamedArg!f);
}


final abstract class Args {
    @property
    static auto opDispatch(string name, Arg)(Arg arg) {
        return NamedArg!(name, Arg)(arg);
    }
}

template staticFilter(alias F, T...) {
    static if (T.length == 0) {
        alias staticFilter = TypeTuple!();
    } else static if (T.length == 1) {
        static if (F!T) {
            alias staticFilter = TypeTuple!(T);
        } else {
            alias staticFilter = TypeTuple!();
        }
    } else {
        alias staticFilter = TypeTuple!(
            staticFilter!(F, T[0..$/2]),
            staticFilter!(F, T[$/2..$]),
            );
    }
}

template Head(T...) if (T.length) {
    alias Head = TypeTuple!(T[0]);
}

template Tail(T...) {
    static if (T.length) {
        alias Tail = T[1..$];
    } else {
        alias Tail = TypeTuple!();
    }
}

template Chop(size_t block, alias F) {
    alias Chop = TypeTuple!();
}

template Chop(size_t blocks, alias F, T...) if (T.length && T.length % blocks == 0) {
    alias Chop = TypeTuple!(
        F!(T[0..$/blocks]),
        Chop!(blocks-1, F, T[$/blocks..$])
        );
}

template staticZip(size_t N, alias F) {
    alias staticZip = TypeTuple!();
}

template staticZip(size_t N, alias F, T...) {
    static if (T.length == 0) {
        alias staticZip = TypeTuple!();
    } else {
        alias staticZip = TypeTuple!(
            F!(Chop!(N, Head, T)),
            staticZip!(N, F, Chop!(N, Tail, T))
            );
    }
}

final abstract class ArgInfo(string _name, _type, _defaultValue...) if (_defaultValue.length == 1) {
    enum name = _name;
    alias type = _type;
    alias value = _defaultValue;
} 

template staticToString(T...) {
    enum staticToString = T.stringof;
}

template staticNameOf(T) {
    enum staticNameOf = T.name;
}

string sortNamedArgs(string[] realNames, string[] defaults, string[] lookHere, int line = __LINE__, string file = __FILE__) {
    string result = "alias sorted = TypeTuple!(";
    foreach (i, name; realNames) {
        bool found = false;
        foreach (j, needle; lookHere) {
            if (needle == name) {
                if (found) {
                    return "static assert(false, \"" ~ file ~ "(" ~ to!string(line) ~ "): Duplicate parameter '" ~ needle ~ "'.\");";
                }
                found = true;
                result ~= "lookup!(" ~ to!string(j) ~ ", namedArgs), ";
            }
        }
        if (!found) {
            result ~= "lookup!(" ~ to!string(i) ~ ", paramDefaults[unnamedArgs.length..$]), ";
        }
    }
    return result ~ ");";
}

template lookup(int i, T...) {
    alias lookup = T[i];
}

struct Compare(T1, T2, string _name) {
    static if (isNamedArg!T1) {
        alias Type1 = typeof(T1.value);
    } else {
        alias Type1 = T1;
    }
    enum name = _name;
    alias Type2 = T2;
}

template checkTypesImpl(int line = __LINE__, string file = __FILE__, int idx, T...) {
    static if(T.length == 0) {
        enum checkTypesImpl = true;
    } else {
        alias T1 = TypeTuple!(T[0].Type1);
        alias T2 = TypeTuple!(T[0].Type2);
        enum N1 = T[0].name;
        static assert(!is(T1 == TypeTuple!void), file ~ "(" ~ to!string(line) ~ "): Missing value for parameter " ~ N1 ~ ".");
        static assert(is(T2 : T1), file ~ "(" ~ to!string(line) ~ "): type mismatch. Expected " ~ T1.stringof ~ ", got " ~ T2.stringof ~ " for parameter " ~ N1 ~ ".");
        enum checkTypesImpl = checkTypesImpl!(line, file, idx+1, T[1..$]);
    }
}

template checkTypes(int line = __LINE__, string file = __FILE__, int idxStart, T...) {
    enum checkTypes = checkTypesImpl!(line, file, idxStart, staticZip!(3, Compare, T));
}

template nameify(alias fn) {
    auto nameify(int line = __LINE__, string file = __FILE__, T...)(T args) {
        auto namedArgs = staticFilter!(isNamedArg, args);
        auto unnamedArgs = staticFilter!(isNotNamedArg, args);
        static assert(is(typeof(TypeTuple!(unnamedArgs, namedArgs)) == T), "Named arguments must follow unnamed arguments");
        
        alias paramNames = ParameterIdentifierTuple!fn;
        alias paramDefaults = ParameterDefaultValueTuple!fn;
        alias paramTypes = ParameterTypeTuple!fn;
        
        alias allArgs = staticZip!(3, ArgInfo, 
                paramNames[unnamedArgs.length..$],
                paramTypes[unnamedArgs.length..$],
                paramDefaults[unnamedArgs.length..$]);
        
        mixin(sortNamedArgs(
                [paramNames[unnamedArgs.length..$]],
                [staticMap!(staticToString, paramDefaults[unnamedArgs.length..$])],
                [staticMap!(staticNameOf, typeof(namedArgs))],
                line, file));
        
        
        static assert(sorted.length == paramTypes[unnamedArgs.length..$].length);
        static assert(checkTypes!(line, file, unnamedArgs.length, typeof(sorted), paramTypes[unnamedArgs.length..$], paramNames[unnamedArgs.length..$]));
        
        fn(unnamedArgs, sorted);
    }
}

void test(int a, int b, string c = "foo", int d = 14) {
    writeln(a);
    writeln(b);
    writeln(c);
    writeln(d);
}

alias test2 = nameify!test;

void main() {
    test2(1, Args.d = 4, Args.c = "bar", Args.b = 13);
}


More information about the Digitalmars-d mailing list