1 /**
2    This module provides the template metaprogramming variant of compile-time
3    reflection, allowing client code to do type-level computations on the
4    contents of a D module.
5  */
6 module mirror.meta;
7 
8 
9 import mirror.traits: moduleOf;
10 
11 
12 /**
13    Compile-time information on a D module.
14  */
15 template Module(string moduleName) {
16 
17     import mirror.traits: RecursiveTypeTree, RecursiveFieldTypes, FundamentalType, PublicMembers;
18     import std.meta: Alias;
19 
20     mixin(`import `, moduleName, `;`);
21     private alias mod = Alias!(mixin(moduleName));
22 
23     private alias publicMembers = PublicMembers!mod;
24 
25     /// User-defined structs/classes
26     alias Aggregates = aggregates!publicMembers;
27 
28     /// User-defined structs/classes and all types contained in them
29     alias AggregatesTree = RecursiveTypeTree!Aggregates;
30 
31     /// Global variables/enums
32     alias Variables = variables!publicMembers;
33 
34     /// List of functions by symbol - contains overloads for each entry
35     alias FunctionsBySymbol = functionsBySymbol!(mod, publicMembers);
36 
37     /// List of functions by overload - each overload is a separate entry
38     alias FunctionsByOverload = functionsByOverload!(mod, publicMembers);
39 
40     alias AllFunctionReturnTypes = allFunctionReturnTypes!FunctionsByOverload;
41     alias AllFunctionReturnTypesTree = RecursiveTypeTree!AllFunctionReturnTypes;
42 
43     alias AllFunctionParameterTypes = allFunctionParameterTypes!FunctionsByOverload;
44     alias AllFunctionParameterTypesTree = RecursiveTypeTree!AllFunctionParameterTypes;
45 }
46 
47 
48 package template allFunctionReturnTypes(functions...) {
49 
50     import mirror.traits: FundamentalType;
51     import std.traits: ReturnType;
52     import std.meta: staticMap, NoDuplicates;
53 
54     private alias symbol(alias F) = F.symbol;
55 
56     alias allFunctionReturnTypes =
57         NoDuplicates!(staticMap!(FundamentalType,
58                                  staticMap!(ReturnType,
59                                             staticMap!(symbol, functions))));
60 }
61 
62 package template allFunctionParameterTypes(functions...) {
63 
64     import mirror.traits: FundamentalType;
65     import std.traits: Parameters;
66     import std.meta: staticMap, NoDuplicates;
67 
68     private alias symbol(alias F) = F.symbol;
69 
70     alias allFunctionParameterTypes =
71         NoDuplicates!(staticMap!(FundamentalType,
72                                  staticMap!(Parameters,
73                                             staticMap!(symbol, functions))));
74 }
75 
76 
77 // User-defined types
78 package template aggregates(publicMembers...) {
79 
80     import std.meta: staticMap, Filter;
81 
82     private template memberIsType(alias member) {
83         import std.traits: isType;
84         enum memberIsType = isType!(member.symbol);
85     }
86 
87     private alias symbolOf(alias member) = member.symbol;
88 
89     alias aggregates = staticMap!(symbolOf, Filter!(memberIsType, publicMembers));
90 }
91 
92 // Global variables
93 private template variables(publicMembers...) {
94     import std.meta: staticMap, Filter;
95 
96     private enum isVariable(alias member) = is(typeof(member.symbol));
97     private alias toVariable(alias member) = Variable!(typeof(member.symbol), __traits(identifier, member.symbol));
98     alias variables = staticMap!(toVariable, Filter!(isVariable, publicMembers));
99 }
100 
101 /**
102    A global variable.
103  */
104 template Variable(T, string N) {
105     alias Type = T;
106     enum name = N;
107 }
108 
109 
110 private template functionsBySymbol(alias mod, publicMembers...) {
111 
112     import mirror.traits: memberIsRegularFunction;
113     import std.meta: Filter, staticMap;
114 
115     private alias functionMembers = Filter!(memberIsRegularFunction, publicMembers);
116 
117     private alias toFunction(alias member) = FunctionSymbol!(
118         member.symbol,
119         __traits(getProtection, member.symbol).toProtection,
120         __traits(getLinkage, member.symbol).toLinkage,
121         member.identifier,
122         mod,
123         );
124 
125     alias functionsBySymbol = staticMap!(toFunction, functionMembers);
126 }
127 
128 
129 /**
130    A function symbol with nested overloads.
131  */
132 template FunctionSymbol(
133     alias F,
134     Protection P = __traits(getProtection, F).toProtection,
135     Linkage L = __traits(getLinkage, F).toLinkage,
136     string I = __traits(identifier, F),
137     alias Parent = moduleOf!F
138 )
139 {
140     import std.meta: staticMap;
141 
142     alias symbol = F;
143     enum identifier = I;
144     alias parent = Parent;
145 
146     private alias toOverload(alias symbol) = FunctionOverload!(
147         symbol,
148         __traits(getProtection, symbol).toProtection,
149         __traits(getLinkage, symbol).toLinkage,
150         identifier,
151         parent,
152     );
153 
154     alias overloads = staticMap!(toOverload, __traits(getOverloads, parent, identifier));
155 
156     string toString() @safe pure {
157         import std.conv: text;
158         return text(`Function(`, overloads.stringof, ")");
159     }
160 }
161 
162 
163 package template functionsByOverload(alias parent, publicMembers...) {
164 
165     import mirror.traits: memberIsRegularFunction;
166     import std.meta: Filter, staticMap;
167 
168     private alias functionMembers = Filter!(memberIsRegularFunction, publicMembers);
169 
170     private template overload(alias S, string I) {
171         alias symbol = S;
172         enum identifier = I;
173     }
174 
175     private template memberToOverloads(alias member) {
176         private alias overloadSymbols = __traits(getOverloads, parent, member.identifier);
177         private alias toOverload(alias symbol) = overload!(symbol, member.identifier);
178         alias memberToOverloads = staticMap!(toOverload, overloadSymbols);
179     }
180 
181     private alias toFunction(alias overload) = FunctionOverload!(
182         overload.symbol,
183         __traits(getProtection, overload.symbol).toProtection,
184         __traits(getLinkage, overload.symbol).toLinkage,
185         overload.identifier,
186         parent,
187     );
188 
189     alias functionsByOverload = staticMap!(toFunction, staticMap!(memberToOverloads, functionMembers));
190 }
191 
192 
193 /**
194    A specific overload of a function.  In most cases it will be
195    synonymous with the function symbol since most functions aren't
196    overloaded.
197  */
198 template FunctionOverload(
199     alias F,
200     Protection P = __traits(getProtection, F).toProtection,
201     Linkage L = __traits(getLinkage, F).toLinkage,
202     string I = __traits(identifier, F),
203     alias Parent = moduleOf!F
204 )
205 {
206     import std.traits: RT = ReturnType;
207 
208     alias symbol = F;
209     alias protection = P;
210     alias linkage = L;
211     enum identifier = I;
212     alias parent = Parent;
213 
214     alias ReturnType = RT!symbol;
215 
216     private template parametersImpl() {
217         import std.traits: Parameters, ParameterIdentifierTuple, ParameterDefaults;
218         import std.meta: staticMap, aliasSeqOf;
219         import std.range: iota;
220 
221         alias parameter(size_t i) =
222             Parameter!(Parameters!symbol[i], ParameterDefaults!symbol[i], ParameterIdentifierTuple!symbol[i]);
223         alias parametersImpl = staticMap!(parameter, aliasSeqOf!(Parameters!F.length.iota));
224     }
225 
226     alias parameters = parametersImpl!();
227 
228     string toString() @safe pure {
229         import std.conv: text;
230         import std.traits: fullyQualifiedName;
231         return text(`Function(`, fullyQualifiedName!symbol, ", ", protection, ", ", linkage, ")");
232     }
233 }
234 
235 
236 template Parameter(T, alias D, string I) {
237     alias Type = T;
238     alias Default = D;
239     enum identifier = I;
240 }
241 
242 
243 /// Visibilty/protection
244 enum Protection {
245     private_,
246     protected_,
247     public_,
248     export_,
249     package_,
250 }
251 
252 
253 Protection toProtection(in string str) @safe pure {
254     import std.conv: to;
255     return (str ~ "_").to!Protection;
256 }
257 
258 
259 ///
260 enum Linkage {
261     D,
262     C,
263     Cpp,
264     Windows,
265     ObjectiveC,
266     System,
267 }
268 
269 
270 Linkage toLinkage(in string str) @safe pure {
271     import std.conv: to;
272     if(str == "C++") return Linkage.Cpp;
273     return str.to!Linkage;
274 }