1 module introspection.template_; 2 3 import std.traits; 4 import std..string; 5 import std.algorithm; 6 import std.array; 7 8 import introspection.location; 9 import introspection.protection; 10 11 version(unittest) { 12 import fluent.asserts; 13 } 14 15 /// Describes a template parameter 16 struct TemplateParameter { 17 /// the parameter name 18 string name; 19 20 /// 21 string type; 22 23 /// 24 string defaultValue; 25 26 /// 27 bool isVariadic; 28 29 /// The string used to parse the template parameter 30 string original; 31 } 32 33 /// 34 TemplateParameter parseTemplateParameter(string paramString) { 35 TemplateParameter param; 36 param.original = paramString; 37 38 auto beginDefaultValue = paramString.indexOf("="); 39 if(beginDefaultValue != -1) { 40 param.defaultValue = paramString[beginDefaultValue + 1..$].strip; 41 paramString = paramString[0..beginDefaultValue].strip; 42 } 43 44 auto beginVariadic = paramString.indexOf("..."); 45 if(beginVariadic != -1) { 46 param.isVariadic = true; 47 paramString = paramString[0..beginVariadic].strip; 48 } 49 50 auto pieces = paramString.split(" "); 51 52 if(pieces.length == 2) { 53 param.type = pieces[0]; 54 param.name = pieces[1]; 55 } else if(pieces.length == 1) { 56 param.name = pieces[0]; 57 } 58 59 return param; 60 } 61 62 /// 63 TemplateParameter[] parseTemplateParameters(string templateDefinition) { 64 auto begin = templateDefinition.indexOf("(") + 1; 65 auto end = templateDefinition.indexOf(")"); 66 67 auto params = templateDefinition[begin..end].split(","); 68 69 70 return params.map!(a => parseTemplateParameter(a.strip)).array; 71 } 72 73 /// 74 struct Template { 75 /// 76 string name; 77 78 /// 79 TemplateParameter[] templateParameters; 80 81 /// 82 TemplateParameter[] parameters; 83 84 /// 85 Protection protection; 86 87 /// 88 Location location; 89 } 90 91 /// 92 TemplateParameter[] parseParameters(string templateDefinition) { 93 auto templateParamsEnd = templateDefinition.indexOf(")") + 1; 94 templateDefinition = templateDefinition[templateParamsEnd .. $]; 95 96 auto begin = templateDefinition.indexOf("("); 97 auto end = templateDefinition.indexOf(")"); 98 99 if(end == -1 || begin == -1) { 100 return []; 101 } 102 103 auto params = templateDefinition[1..end].split(","); 104 105 106 return params.map!(a => parseTemplateParameter(a.strip)).array; 107 } 108 109 /// Describes a template 110 Template describeTemplate(alias T)() if(__traits(isTemplate, T)) { 111 Template tpl; 112 113 tpl.name = __traits(identifier, T); 114 115 static if(__traits(compiles, T.stringof)) { 116 tpl.templateParameters = parseTemplateParameters(T.stringof); 117 tpl.parameters = parseParameters(T.stringof); 118 } 119 120 auto location = __traits(getLocation, T); 121 tpl.location = Location(location[0], location[1], location[2]); 122 123 tpl.protection = __traits(getProtection, T).toProtection; 124 125 return tpl; 126 } 127 128 /// It should describe the template name and parameters 129 unittest { 130 template foo(T) { 131 alias foo = string; 132 } 133 134 auto result = describeTemplate!foo; 135 136 result.name.should.equal("foo"); 137 result.parameters.length.should.equal(0); 138 139 result.templateParameters.length.should.equal(1); 140 result.templateParameters[0].name.should.equal("T"); 141 result.templateParameters[0].original.should.equal("T"); 142 result.templateParameters[0].type.should.equal(""); 143 result.templateParameters[0].defaultValue.should.equal(""); 144 result.templateParameters[0].isVariadic.should.equal(false); 145 146 result.protection.should.equal(Protection.public_); 147 148 result.location.file.should.equal("source/introspection/template_.d"); 149 result.location.line.should.be.greaterThan(0); 150 result.location.column.should.equal(3); 151 } 152 153 /// It should describe a templated function 154 unittest { 155 void foo(T)(T param) { } 156 157 auto result = describeTemplate!foo; 158 159 result.name.should.equal("foo"); 160 result.templateParameters.length.should.equal(1); 161 162 result.templateParameters[0].name.should.equal("T"); 163 result.templateParameters[0].original.should.equal("T"); 164 result.templateParameters[0].type.should.equal(""); 165 result.templateParameters[0].defaultValue.should.equal(""); 166 result.templateParameters[0].isVariadic.should.equal(false); 167 168 result.parameters.length.should.equal(1); 169 result.parameters[0].name.should.equal("param"); 170 result.parameters[0].original.should.equal("T param"); 171 result.parameters[0].type.should.equal("T"); 172 result.parameters[0].defaultValue.should.equal(""); 173 result.parameters[0].isVariadic.should.equal(false); 174 } 175 176 /// It should describe a templated function with default values 177 unittest { 178 void foo(T = int)(T param) { } 179 180 auto result = describeTemplate!foo; 181 182 result.name.should.equal("foo"); 183 result.templateParameters.length.should.equal(1); 184 185 result.templateParameters[0].name.should.equal("T"); 186 result.templateParameters[0].defaultValue.should.equal("int"); 187 result.templateParameters[0].original.should.equal("T = int"); 188 result.templateParameters[0].type.should.equal(""); 189 result.templateParameters[0].isVariadic.should.equal(false); 190 191 result.parameters.length.should.equal(1); 192 result.parameters[0].name.should.equal("param"); 193 result.parameters[0].original.should.equal("T param"); 194 result.parameters[0].type.should.equal("T"); 195 result.parameters[0].defaultValue.should.equal(""); 196 result.parameters[0].isVariadic.should.equal(false); 197 } 198 199 /// It should describe a templated function with a numeric parameter and variadic parameters 200 unittest { 201 void foo(int a = 3, T...)(int b) if(T.length > 1) { } 202 203 auto result = describeTemplate!foo; 204 205 result.name.should.equal("foo"); 206 result.templateParameters.length.should.equal(2); 207 208 result.templateParameters[0].name.should.equal("a"); 209 result.templateParameters[0].defaultValue.should.equal("3"); 210 result.templateParameters[0].original.should.equal("int a = 3"); 211 result.templateParameters[0].type.should.equal("int"); 212 result.templateParameters[0].isVariadic.should.equal(false); 213 214 result.templateParameters[1].name.should.equal("T"); 215 result.templateParameters[1].defaultValue.should.equal(""); 216 result.templateParameters[1].original.should.equal("T..."); 217 result.templateParameters[1].type.should.equal(""); 218 result.templateParameters[1].isVariadic.should.equal(true); 219 220 result.parameters.length.should.equal(1); 221 result.parameters[0].name.should.equal("b"); 222 result.parameters[0].original.should.equal("int b"); 223 result.parameters[0].type.should.equal("int"); 224 result.parameters[0].defaultValue.should.equal(""); 225 result.parameters[0].isVariadic.should.equal(false); 226 } 227 228 229 /// It should describe a templated class with a numeric parameter and variadic parameters 230 unittest { 231 class Foo(int a = 3, T...) { 232 void bar() {} 233 } 234 235 auto result = describeTemplate!Foo; 236 237 result.name.should.equal("Foo"); 238 result.templateParameters.length.should.equal(2); 239 240 result.templateParameters[0].name.should.equal("a"); 241 result.templateParameters[0].defaultValue.should.equal("3"); 242 result.templateParameters[0].original.should.equal("int a = 3"); 243 result.templateParameters[0].type.should.equal("int"); 244 result.templateParameters[0].isVariadic.should.equal(false); 245 246 result.templateParameters[1].name.should.equal("T"); 247 result.templateParameters[1].defaultValue.should.equal(""); 248 result.templateParameters[1].original.should.equal("T..."); 249 result.templateParameters[1].type.should.equal(""); 250 result.templateParameters[1].isVariadic.should.equal(true); 251 }