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 }