1 /**
2    This module provides the CTFE variant of compile-time reflection,
3    allowing client code to use regular D functions (as opposed to
4    template metaprogramming) to operate on the contents of a D module
5    using string mixins.
6  */
7 
8 module mirror.ctfe;
9 
10 
11 /**
12    Returns compile-time reflection information about a D module.
13  */
14 Module module_(string moduleName)() {
15     import mirror.meta: ModuleTemplate = Module;
16     import std.meta: staticMap;
17 
18     Module ret;
19     ret.identifier = moduleName;
20 
21     alias module_ = ModuleTemplate!moduleName;
22 
23     template toKind(T) {
24         import mirror.traits: FundamentalType;
25         alias U = FundamentalType!T;
26         static if(is(U == enum))
27             enum toKind = Aggregate.Kind.enum_;
28         else static if(is(U == struct))
29             enum toKind = Aggregate.Kind.struct_;
30         else static if(is(U == class))
31             enum toKind = Aggregate.Kind.class_;
32         else static if(is(U == interface))
33             enum toKind = Aggregate.Kind.interface_;
34         else
35             static assert(false, "Unknown kind " ~ T.stringof);
36     }
37 
38     enum toAggregate(T) = Aggregate(T.stringof, toKind!T);
39     ret.aggregates = [ staticMap!(toAggregate, module_.Aggregates) ];
40     ret.allAggregates = [ staticMap!(toAggregate, module_.AllAggregates) ];
41 
42     enum toVariable(alias V) = Variable(V.Type.stringof, V.identifier);
43     ret.variables = [ staticMap!(toVariable, module_.Variables) ];
44 
45     template toFunction(alias F) {
46 
47         import std.range: iota;
48         import std.meta: aliasSeqOf;
49         import std.traits: ReturnType, Parameters, ParameterDefaults, ParameterIdentifierTuple;
50 
51         template toDefault(size_t i) {
52             static if(is(ParameterDefaults!(F.symbol)[i] == void))
53                 enum toDefault = "";
54             else
55                 enum toDefault = ParameterDefaults!(F.symbol)[i].stringof;
56         }
57 
58         template toParameter(size_t i) {
59             import std.traits: ParameterStorageClassTuple;
60 
61             enum toParameter = Parameter(
62                 Parameters!(F.symbol)[i].stringof,
63                 ParameterIdentifierTuple!(F.symbol)[i],
64                 toDefault!i,
65                 ParameterStorageClassTuple!(F.symbol)[i],
66             );
67         }
68 
69         enum toFunction = Function(
70             moduleName,
71             F.index,
72             F.identifier,
73             Type(ReturnType!(F.symbol).stringof),
74             [staticMap!(toParameter, aliasSeqOf!(Parameters!(F.symbol).length.iota))],
75         );
76     }
77 
78     ret.functionsByOverload = [ staticMap!(toFunction, module_.FunctionsByOverload) ];
79 
80     template withIndex(A...) {
81         import std.range: iota;
82         import std.meta: aliasSeqOf;
83 
84         template overload(alias F, size_t I) {
85             alias symbol = F.symbol;
86             enum identifier = F.identifier;
87             enum index = I;
88         }
89 
90         alias toOverload(size_t I) = overload!(A[I], I);
91 
92         alias withIndex = staticMap!(toOverload, aliasSeqOf!(A.length.iota));
93     }
94 
95     template toOverloaded(alias F) {
96         enum toOverloaded = OverloadSet(
97             F.identifier,
98             [ staticMap!(toFunction, withIndex!(F.overloads)) ]
99         );
100     }
101 
102     ret.functionsBySymbol = [ staticMap!(toOverloaded, module_.FunctionsBySymbol) ];
103 
104     return ret;
105 }
106 
107 
108 /**
109    A D module.
110  */
111 struct Module {
112     string identifier;
113     Aggregate[] aggregates;
114     Aggregate[] allAggregates;  // includes all function return types
115     Variable[] variables;
116     Function[] functionsByOverload;
117     OverloadSet[] functionsBySymbol;
118 }
119 
120 
121 /**
122    A user-defined type (struct, class, or enum).
123  */
124 struct Aggregate {
125 
126     enum Kind {
127         enum_,
128         struct_,
129         class_,
130         interface_,
131     }
132 
133     string identifier;
134     Kind kind;
135     Variable[] fields;
136     Function[] functions;
137     // UDAs?
138 }
139 
140 struct Type {
141     string identifier;
142     // UDAs?
143     string toString() @safe @nogc pure nothrow const {
144         return identifier;
145     }
146 }
147 
148 /// A variable
149 struct Variable {
150     string type;
151     string identifier;
152     // UDAs?
153 }
154 
155 
156 /// A set of function overloads
157 struct OverloadSet {
158     string identifier;
159     Function[] overloads;
160 }
161 
162 /// A function
163 struct Function {
164     string moduleName;
165     int overloadIndex;
166     string identifier;
167     Type returnType;
168     Parameter[] parameters;
169     // TODO: @safe, pure, nothrow, etc.
170     // TODO: UDAs
171 
172 
173     string importMixin() @safe pure nothrow const {
174         return `static import ` ~ moduleName ~ `;`;
175     }
176 
177     string callMixin(A...)(auto ref A args) {
178         import std.conv: text;
179         import std.array: join;
180         import std.algorithm: map;
181 
182         string[] argTexts;
183 
184         static foreach(arg; args) {
185             argTexts ~= arg.text;
186         }
187 
188         return text(
189             moduleName, `.`, identifier, `(`,
190             argTexts.map!text.join(`, `),
191             `)`);
192     }
193 
194     string fullyQualifiedName() @safe pure nothrow const {
195         return moduleName ~ "." ~ identifier;
196     }
197 
198     string pointerMixin() @safe pure nothrow const {
199         import std.conv: text;
200         return text(`&__traits(getOverloads, `, moduleName, `, "`, identifier, `")[`, overloadIndex, `]`);
201     }
202 }
203 
204 auto pointer(Function function_)() {
205     mixin(`static import `, function_.moduleName, `;`);
206 
207     alias overloads = __traits(
208         getOverloads,
209         mixin(function_.moduleName),
210         function_.identifier
211     );
212 
213     return &overloads[function_.overloadIndex];
214 }
215 
216 
217 /// A function parameter
218 struct Parameter {
219     import std.traits: ParameterStorageClass;
220 
221     string type;
222     string identifier;
223     string default_;  /// default value, if any
224     ParameterStorageClass storageClass;
225 }
226 
227 
228 // TODO:
229 // * Module {c,d}tors
230 // * Unit tests
231 // * Class hierachies
232 // * Aliases?