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?