1 ///
2 module rxd.meta2.type;
3 
4 import std.traits : isInstanceOf;
5 
6 ///
7 enum isRawType(T...) = T.length == 1 &&
8     is(T[0]) && !is(T[0] : Type!U, U);
9 
10 ///
11 enum isType(T...) = T.length == 1 && is(T[0] : Type!U, U);
12 
13 ///
14 enum isAliasTuple(T) = isInstanceOf!(AliasTuple, T);
15 
16 ///
17 auto aliasTuple(Types...)()
18 {
19     return AliasTuple!Types();
20 }
21 
22 template TypeSeq(T...)
23 {
24     import std.meta : AliasSeq;
25     static if (T.length == 0)
26         alias TypeSeq = AliasSeq!();
27 
28     else
29         alias TypeSeq = AliasSeq!(Type!(T[0]), TypeSeq!(T[1 .. $]));
30 }
31 
32 ///
33 auto val(T...)() if (T.length == 1)
34 {
35     return Val!(T[0]).init;
36 }
37 
38 ///
39 struct Val(T...) if (T.length == 1)
40 {
41     static assert (!is(T[0]),
42         "Expected value, instead of type: " ~ T[0].stringof ~ "!");
43 
44     ///
45     alias Type = typeof(T[0]);
46 
47     ///
48     enum typeOf = type!Type;
49 
50     ///
51     enum Type val = T[0];
52 
53     alias val this;
54 }
55 
56 ///
57 struct ValTuple(vals...)
58 {
59     enum expand = vals;
60 }
61 
62 ///
63 auto type(T)()
64 {
65     return Type!T.init;
66 }
67 
68 /// ditto
69 alias Type(T : Type!U, U) = T;
70 
71 ///
72 unittest
73 {
74     static assert (is(Type!(Type!int) == Type!int));
75 }
76 
77 /// ditto
78 struct Type(T)
79 {
80     ///
81     alias typeOf = T;
82 
83     const pure nothrow @safe @nogc:
84 
85     ///
86     bool opEquals(U)(Type!U other)
87     { return is(T == U); }
88 
89     ///
90     auto opSlice()()
91     { return Type!(T[]).init; }
92 
93     ///
94     auto opIndex(S)(S sizeVal) if (is(S : Val!size, size_t size))
95     { return Type!(T[sizeVal.val]).init; }
96 
97     ///
98     auto opIndex(U)(Type!U keyType)
99     { return Type!(T[keyType.typeOf]).init; }
100 
101     auto opBinary(string op, U)(Type!U other)
102         if (op == "+" || op == "*")
103     {
104         import std.typecons : Tuple;
105         import std.variant : Algebraic;
106 
107         static if (op == "+")
108             return Type!(Algebraic!(T, U)).init;
109 
110         else static if (op == "*")
111             return Type!(Tuple!(T, U)).init;
112 
113     }
114 
115     static if (is(typeOf == int))
116     ///
117     unittest
118     {
119         import std.typecons : Tuple;
120         import std.variant : Algebraic;
121 
122         enum t1 = type!int;
123         enum t2 = type!double;
124 
125         enum t3 = t1 + t2;
126         static assert (t3 == type!(Algebraic!(int, double)));
127 
128         enum t4 = t1 * t2;
129         static assert (t4 == type!(Tuple!(int, double)));
130 
131         enum t5 = (t1 + t2) * t1 + t4;
132         static assert (t5 ==
133             type!(
134                 Algebraic!(
135                     Tuple!(
136                         Algebraic!(int, double),
137                         int
138                     ),
139                     Tuple!(int, double)
140                 )
141             )
142         );
143     }
144 
145     ///
146     auto unqualOf()
147     {
148         import std.traits : Unqual;
149         return Type!(Unqual!T).init;
150     }
151 
152     ///
153     auto constOf()()
154     { return Type!(const(T)).init; }
155 
156     ///
157     auto immutableOf()()
158     { return Type!(immutable(T)).init; }
159 
160     ///
161     auto sharedOf()()
162     { return Type!(shared(T)).init; }
163 
164     /// Convience function that forwards to std.traits.
165     auto opDispatch(string traitName)()
166     {
167         import std.meta : AliasSeq, allSatisfy;
168         import std.traits;
169 
170         alias res = AliasSeq!(mixin(traitName ~ "!(this.typeOf)"));
171 
172         static if (res.length == 1 && is(typeof(res[0])))
173             // Return values directly
174             return res[0];
175 
176         else static if (res.length == 1 && is(res[0]))
177             // Wrap types in Type(T)
178             return type!res;
179 
180         else static if (allSatisfy!(isRawType, res))
181             // Return AliasTuple
182             return aliasTuple!(TypeSeq!res);
183 
184         else
185             // Wrap value tuples in ValTuple
186             return ValTuple!res.init;
187     }
188 
189     static if (is(T == int))
190     ///
191     unittest
192     {
193         enum t1 = type!int;
194         static assert (t1.opDispatch!"isIntegral");
195 
196         static assert ( type!float.isFloatingPoint);
197         static assert (!type!float.isBoolean);
198 
199         enum t2 = type!string[type!int[]];
200         static assert (t2 == type!(string[int[]]));
201         static assert ( t2.isAssociativeArray);
202         static assert (!t2.isArray);
203 
204         enum t3 = type!(byte*);
205         static assert (t3.PointerTarget == type!byte);
206 
207         enum Names : string { n1 = "asd", n2 = "bfg" }
208         enum t4 = type!Names;
209         static assert (t4.OriginalType == type!string);
210         enum members = t4.EnumMembers;
211         static assert ([t4.EnumMembers.expand] == [Names.n1, Names.n2]);
212 
213         class C0 {}
214         class C1 : C0 {}
215         class C2 : C1 {}
216         class C3 : C2 {}
217 
218         enum t5 = type!C3;
219         static assert (t5.BaseClassesTuple ==
220             aliasTuple!(Type!C2, Type!C1, Type!C0, Type!Object));
221 
222         interface I0 { }
223         interface I1 { }
224         interface I2 { }
225         interface I12 : I0, I1, I2 { }
226         interface I12_12 : I1, I2, I12 {}
227 
228         enum t6 = type!I12_12;
229         static assert (t6.InterfacesTuple ==
230             aliasTuple!(Type!I1, Type!I2, Type!I12, Type!I0));
231     }
232 
233     ///
234     string toString()
235     {
236         return typeof(this).stringof;
237     }
238 }
239 
240 unittest
241 {
242     enum t1 = type!int;
243     enum t2 = t1.constOf;
244     enum t3 = t2.sharedOf;
245 
246     static assert (is(typeof(t1) == Type!int));
247     static assert (is(t1.typeOf == int));
248     static assert (is(typeof(t2) == Type!(const(int))));
249     static assert (is(t3.typeOf == shared(const(int))));
250     static assert (t3.unqualOf == t1);
251 
252     // operator opSlice() ( [] ) yields an array
253     enum t4 = t1[];
254     static assert (is(t4.typeOf == int[]));
255 
256     // can easily chain methods to generate complex types
257     enum t5 = t1.immutableOf[].sharedOf;
258 
259     // operator opEquals(Type!U) ( == ) can be use check if two types
260     // are the same
261     static assert (t5 == type!(shared(immutable(int)[])));
262 
263     // operator opIndex(Type!U) ( e.g. [ Type!string ] ) can be used to
264     // generate associative arrays
265     enum t6 = t1[type!string];
266     static assert (t6.isAssociativeArray);
267     static assert (t6 == type!(int[string]));
268 
269     // operator opIndex(Val!size) ( e.g. [ Val!5 ] ) can be used to
270     // generate static arrays
271     enum t7 = t1[val!4];
272     static assert (t7.isStaticArray);
273     static assert (t7 == type!(int[4]));
274 }
275 
276 ///
277 struct AliasTuple(Types...)
278 {
279     //Types expand;
280     //alias expand this;
281     alias typeOf = typeof(this);
282     alias typeSeq = SeqFromTuple!(typeOf);
283     alias typeTypeSeq = Types;
284 
285     enum empty = Types.length == 0;
286 
287     static if (empty)
288     {
289         // Special case allowing
290         // ElementType!(typeof(this)) == TypeTuple!()
291         // in generic code.
292 
293         auto front()() { return this; }
294         auto back()() { return this; }
295         auto dropOne()() { static assert (0); }
296         auto dropBackOne()() { static assert (0); }
297     }
298     else
299     {
300         auto front()() { return Types[0].init; }
301         auto back()() { return Types[$ - 1].init; }
302         auto dropOne()() { return AliasTuple!(Types[1 .. $]).init; }
303         auto dropBackOne()() { return AliasTuple!(Types[0 .. $ - 1]).init; }
304     }
305 
306     string toString()
307     {
308         return typeOf.stringof;
309     }
310 }
311 
312 template SeqFromTuple(Tuple : AliasTuple!TL, TL...)
313 {
314     import std.meta : AliasSeq;
315 
316     static if (TL.length == 0)
317         alias SeqFromTuple = AliasSeq!();
318     else
319         alias SeqFromTuple = AliasSeq!(
320                 TL[0].typeOf,
321                 SeqFromTuple!(Tuple.init.dropOne().typeOf)
322         );
323 
324 }
325 
326 auto append(T, TT)(TT tuple, T type)
327 {
328     return AliasTuple!(tuple.typeTypeSeq, T)();
329 }