root/trunk/infrastructure/meta/Demangle.d

Revision 120, 24.5 kB (checked in by KirkMcDonald, 1 year ago)

* Pyd now requires D 2.003 or later.
* Pyd now compiles with D 2.003.
* Resolved long-standing string-copying annoyance (thanks to const).

Line 
1 /**
2  *  Demangle a ".mangleof" name at compile time.
3  *
4  * Used by meta.Nameof.
5  *
6  * License:   BSD style: $(LICENSE)
7  * Authors:   Don Clugston
8  * Copyright: Copyright (C) 2005-2006 Don Clugston
9  */
10 module meta.Demangle;
11 /*
12  Implementation is via pairs of metafunctions:
13  a 'demangle' metafunction, which returns a const char [],
14  and a 'Consumed' metafunction, which returns an integer, the number of characters which
15  are used.
16 */
17
18 /*****************************************
19  * How should the name be displayed?
20  */
21 enum MangledNameType
22 {
23     PrettyName,    // With full type information
24     QualifiedName, // No type information, just identifiers seperated by dots
25     SymbolName     // Only the ultimate identifier
26 }
27
28 /*****************************************
29  * Pretty-prints a mangled type string.
30  */
31 template demangleType(string str, MangledNameType wantQualifiedNames = MangledNameType.PrettyName)
32 {
33     static if (wantQualifiedNames != MangledNameType.PrettyName) {
34         // There are only a few types where symbolnameof!(), qualifiednameof!()
35         // make sense.
36         static if (str[0]=='C' || str[0]=='S' || str[0]=='E' || str[0]=='T')
37             const char [] demangleType = prettyLname!(str[1..$], wantQualifiedNames);
38         else {
39             static assert(0, "Demangle error: type '" ~ str ~ "' does not contain a qualified name");
40         }
41     } else static if (str[0] == 'A') // dynamic array
42         const char [] demangleType = demangleType!(str[1..$], wantQualifiedNames) ~ "[]";
43     else static if (str[0] == 'H')   // associative array
44         const char [] demangleType
45             = demangleType!(str[1+demangleTypeConsumed!(str[1..$])..$], wantQualifiedNames)
46             ~ "[" ~ demangleType!(str[1..1+(demangleTypeConsumed!(str[1..$]))], wantQualifiedNames) ~ "]";
47     else static if (str[0] == 'G') // static array
48         const char [] demangleType
49             = demangleType!(str[1+countLeadingDigits!(str[1..$])..$], wantQualifiedNames)
50             ~ "[" ~ str[1..1+countLeadingDigits!(str[1..$]) ] ~ "]";
51     else static if (str[0]=='C')
52         const char [] demangleType = "class " ~ prettyLname!(str[1..$], wantQualifiedNames);
53     else static if (str[0]=='S')
54         const char [] demangleType = "struct " ~ prettyLname!(str[1..$], wantQualifiedNames);
55     else static if (str[0]=='E')
56         const char [] demangleType = "enum " ~ prettyLname!(str[1..$], wantQualifiedNames);
57     else static if (str[0]=='T')
58         const char [] demangleType = "typedef " ~ prettyLname!(str[1..$], wantQualifiedNames);
59     else static if (str[0]=='D' && str.length>2 && isMangledFunction!(( str[1] )) ) // delegate
60         const char [] demangleType = demangleFunctionOrDelegate!(str[1..$], "delegate ", wantQualifiedNames);
61     else static if (str[0]=='P' && str.length>2 && isMangledFunction!(( str[1] )) ) // function pointer
62         const char [] demangleType = demangleFunctionOrDelegate!(str[1..$], "function ", wantQualifiedNames);
63     else static if (str[0]=='P') // only after we've dealt with function pointers
64         const char [] demangleType = demangleType!(str[1..$], wantQualifiedNames) ~ "*";
65     else static if (str[0]=='F')
66         const char [] demangleType = demangleFunctionOrDelegate!(str, "", wantQualifiedNames);
67     else const char [] demangleType = demangleBasicType!(str);
68 }
69
70 // split these off because they're numerous and simple
71 // Note: For portability, could replace "v" with void.mangleof, etc.
72 template demangleBasicType(string str)
73 {
74          static if (str == "v") const char [] demangleBasicType = "void";
75     else static if (str == "b") const char [] demangleBasicType = "bool";
76     // possibly a bug in the D name mangling algorithm?
77     else static if (str == "x") const char [] demangleBasicType = "bool";
78
79     // integral types
80     else static if (str == "g") const char [] demangleBasicType = "byte";
81     else static if (str == "h") const char [] demangleBasicType = "ubyte";
82     else static if (str == "s") const char [] demangleBasicType = "short";
83     else static if (str == "t") const char [] demangleBasicType = "ushort";
84     else static if (str == "i") const char [] demangleBasicType = "int";
85     else static if (str == "k") const char [] demangleBasicType = "uint";
86     else static if (str == "l") const char [] demangleBasicType = "long";
87     else static if (str == "m") const char [] demangleBasicType = "ulong";
88     // floating point
89     else static if (str == "e") const char [] demangleBasicType = "real";
90     else static if (str == "d") const char [] demangleBasicType = "double";
91     else static if (str == "f") const char [] demangleBasicType = "float";
92
93     else static if (str == "j") const char [] demangleBasicType = "ireal";
94     else static if (str == "p") const char [] demangleBasicType = "idouble";
95     else static if (str == "o") const char [] demangleBasicType = "ifloat";
96
97     else static if (str == "c") const char [] demangleBasicType = "creal";
98     else static if (str == "r") const char [] demangleBasicType = "cdouble";
99     else static if (str == "q") const char [] demangleBasicType = "cfloat";
100     // Char types
101     else static if (str == "a") const char [] demangleBasicType = "char";
102     else static if (str == "u") const char [] demangleBasicType = "wchar";
103     else static if (str == "w") const char [] demangleBasicType = "dchar";
104
105     else static assert(0, "Demangle Error: '" ~ str ~ "' is not a recognised basic type");
106 }
107
108 template demangleTypeConsumed(string str)
109 {
110     static if (str[0]=='A')
111         const int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
112     else static if (str[0]=='H')
113         const int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$])
114             + demangleTypeConsumed!(str[1+demangleTypeConsumed!(str[1..$])..$]);
115     else static if (str[0]=='G')
116         const int demangleTypeConsumed = 1 + countLeadingDigits!(str[1..$])
117             + demangleTypeConsumed!( str[1+countLeadingDigits!(str[1..$])..$] );
118     else static if (str.length>2 && (str[0]=='P' || str[0]=='D') && isMangledFunction!(( str[1] )) )
119         const int demangleTypeConsumed = 2 + demangleParamListAndRetValConsumed!(str[2..$]);
120     else static if (str[0]=='P') // only after we've dealt with function pointers
121         const int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
122     else static if (str[0]=='C' || str[0]=='S' || str[0]=='E' || str[0]=='T')
123         const int demangleTypeConsumed = 1 + getQualifiedNameConsumed!(str[1..$]);
124     else static if (str[0]=='F' && str.length>1)
125         const int demangleTypeConsumed = 1 + demangleParamListAndRetValConsumed!(str[1..$]);
126     else // it's a Basic Type
127         const int demangleTypeConsumed = 1;
128 }
129
130 // --------------------------------------------
131 //              STATIC ARRAYS
132
133 // For static arrays, count number of digits used (eg, return 3 for "674")
134 template countLeadingDigits(string str)
135 {
136     static if (str.length>0 && beginsWithDigit!( str))
137         const int countLeadingDigits = 1 + countLeadingDigits!( str[1..$]);
138     else const int countLeadingDigits = 0;
139 }
140
141 // --------------------------------------------
142 //              LNAMES
143
144 // str must start with an Lname: first chars give the length
145 // reads the digits from the front of str, gets the Lname
146 // Sometimes the characters following the length are also digits!
147 // (this happens with templates, when the name being 'lengthed' is itself an Lname).
148 // We guard against this by ensuring that the L is less than the length of the string.
149 template getLname(string str)
150 {
151     static if (str.length <= 9+1 || !beginsWithDigit!(str[1..$]) )
152         const char [] getLname = str[1..(str[0]-'0' + 1)];
153     else static if (str.length <= 99+2 || !beginsWithDigit!(str[2..$]) )
154         const char [] getLname = str[2..((str[0]-'0')*10 + str[1]-'0'+ 2)];
155     else static if (str.length <= 999+3 || !beginsWithDigit!(str[3..$]) )
156         const char [] getLname =
157             str[3..((str[0]-'0')*100 + (str[1]-'0')*10 + str[2]-'0' + 3)];
158     else
159         const char [] getLname =
160             str[4..((str[0]-'0')*1000 + (str[1]-'0')*100 + (str[2]-'0')*10 + (str[3]-'0') + 4)];
161 }
162
163 // Deal with the case where an Lname contains an embedded "__D".
164 // This can happen when classes, typedefs, etc are declared inside a function.
165 template pretty_Dname(string str, int dotnameconsumed, MangledNameType wantQualifiedNames)
166 {
167     static if ( isMangledFunction!( (str[2+dotnameconsumed]))) {
168         const char [] pretty_Dname = pretty_Dfunction!(str, dotnameconsumed,
169             demangleParamListAndRetValConsumed!(str[3+dotnameconsumed..$]), wantQualifiedNames);
170     } else {
171         static if (wantQualifiedNames == MangledNameType.PrettyName) {
172             const char [] pretty_Dname =
173                 demangleType!(str[2+dotnameconsumed..$], wantQualifiedNames)
174                 ~ " " ~ getQualifiedName!(str[2..$], wantQualifiedNames);
175         } else {
176             const char [] pretty_Dname = getQualifiedName!(str[2..$], wantQualifiedNames);
177         }
178     }
179 }
180
181 // Deal with the case where an Lname contains an embedded ("__D") function.
182 // Split into a seperate function because it's so complicated.
183 template pretty_Dfunction(string str, int dotnameconsumed, int paramlistconsumed,
184     MangledNameType wantQualifiedNames)
185 {
186     static if (wantQualifiedNames == MangledNameType.PrettyName) {
187         const char [] pretty_Dfunction =
188             demangleFunctionOrDelegate!(str[2 + dotnameconsumed .. 3 + dotnameconsumed + paramlistconsumed],
189                 getQualifiedName!(str[2..2+dotnameconsumed], wantQualifiedNames), wantQualifiedNames)
190                 // BUG: This shouldn't be necessary, the string length is wrong somewhere
191             ~ getQualifiedName!(str[3 + dotnameconsumed + paramlistconsumed .. $], wantQualifiedNames, ".");
192     } else static if (wantQualifiedNames == MangledNameType.QualifiedName) {
193         // Qualified name
194         const char [] pretty_Dfunction = getQualifiedName!(str[2..2+dotnameconsumed], wantQualifiedNames)
195             ~ getQualifiedName!(str[3 + dotnameconsumed + paramlistconsumed .. $], wantQualifiedNames, ".");
196     } else { // symbol name
197         static if (3 + dotnameconsumed + paramlistconsumed == str.length)
198             const char [] pretty_Dfunction = getQualifiedName!(str[2..2+dotnameconsumed], wantQualifiedNames);
199         else const char [] pretty_Dfunction = getQualifiedName!(
200             str[3 + dotnameconsumed + paramlistconsumed .. $], wantQualifiedNames);
201     }
202  }
203
204 // for an Lname that begins with "_D"
205 template get_DnameConsumed(string str)
206 {
207     const int get_DnameConsumed = 2 + getQualifiedNameConsumed!(str[2..$])
208         + demangleTypeConsumed!(str[2+getQualifedNameConsumed!(str[2..$])..$]);
209 }
210
211 // If Lname is a template, shows it as a template
212 template prettyLname(string str, MangledNameType wantQualifiedNames)
213 {
214     static if (str.length>3 && str[0..3] == "__T") // Template instance name
215         static if (wantQualifiedNames == MangledNameType.PrettyName) {
216             const char [] prettyLname =
217                 prettyLname!(str[3..$], wantQualifiedNames) ~ "!("
218                 ~ prettyTemplateArgList!(str[3+getQualifiedNameConsumed!(str[3..$])..$], wantQualifiedNames)
219                 ~ ")";
220         } else {
221             const char [] prettyLname =
222                 prettyLname!(str[3..$], wantQualifiedNames);
223         }
224     else static if (str.length>2 && str[0..2] == "_D") {
225         const char [] prettyLname = pretty_Dname!(str, getQualifiedNameConsumed!(str[2..$]), wantQualifiedNames);
226     } else static if ( beginsWithDigit!( str ) )
227         const char [] prettyLname = getQualifiedName!(str[0..getQualifiedNameConsumed!(str)], wantQualifiedNames);
228     else const char [] prettyLname = str;
229 }
230
231 // str must start with an lname: first chars give the length
232 // how many chars are taken up with length digits + the name itself
233 template getLnameConsumed(string str)
234 {
235     static if (str.length==0)
236         const int getLnameConsumed=0;
237     else static if (str.length <= (9+1) || !beginsWithDigit!(str[1..$]) )
238         const int getLnameConsumed = 1 + str[0]-'0';
239     else static if (str.length <= (99+2) || !beginsWithDigit!( str[2..$]) )
240         const int getLnameConsumed = (str[0]-'0')*10 + str[1]-'0' + 2;
241     else static if (str.length <= (999+3) || !beginsWithDigit!( str[3..$]) )
242         const int getLnameConsumed = (str[0]-'0')*100 + (str[1]-'0')*10 + str[2]-'0' + 3;
243     else
244         const int getLnameConsumed = (str[0]-'0')*1000 + (str[1]-'0')*100 + (str[2]-'0')*10 + (str[3]-'0') + 4;
245 }
246
247 template getQualifiedName(string str, MangledNameType wantQualifiedNames, string dotstr = "")
248 {
249     static if (str.length==0) const char [] getQualifiedName="";
250 //    else static if (str.length>2 && str[0]=='_' && str[1]=='D')
251 //        const char [] getDotName = getQualifiedName!(str[2..$], wantQualifiedNames);
252     else {
253         static assert (beginsWithDigit!(str));
254         static if ( getLnameConsumed!(str) < str.length && beginsWithDigit!(str[getLnameConsumed!(str)..$]) ) {
255             static if (wantQualifiedNames == MangledNameType.SymbolName) {
256                 // For symbol names, only display the last symbol
257                 const char [] getQualifiedName =
258                     getQualifiedName!(str[getLnameConsumed!(str) .. $], wantQualifiedNames, "");
259             } else {
260                 // Qualified and pretty names display everything
261                 const char [] getQualifiedName = dotstr
262                     ~ prettyLname!(getLname!(str), wantQualifiedNames)
263                     ~ getQualifiedName!(str[getLnameConsumed!(str) .. $], wantQualifiedNames, ".");
264             }
265         } else {
266             const char [] getQualifiedName = dotstr ~ prettyLname!(getLname!(str), wantQualifiedNames);
267         }
268     }
269 }
270
271 template getQualifiedNameConsumed (string str)
272 {
273     static if ( str.length>1 &&  beginsWithDigit!(str) ) {
274         static if (getLnameConsumed!(str) < str.length && beginsWithDigit!( str[getLnameConsumed!(str)..$])) {
275             const int getQualifiedNameConsumed = getLnameConsumed!(str)
276                 + getQualifiedNameConsumed!(str[getLnameConsumed!(str) .. $]);
277         } else {
278             const int getQualifiedNameConsumed = getLnameConsumed!(str);
279         }
280     } /*else static if (str.length>1 && str[0]=='_' && str[1]=='D') {
281         const int getQualifiedNameConsumed = get_DnameConsumed!(str)
282             + getQualifiedNameConsumed!(str[1+get_DnameConsumed!(str)..$]);
283     }*/ else static assert(0);
284 }
285
286 // ----------------------------------------
287 //              FUNCTIONS
288
289 /* str[0] must indicate the extern linkage of the function. funcOrDelegStr is the name of the function,
290 * or "function " or "delegate "
291 */
292 template demangleFunctionOrDelegate(string str, string funcOrDelegStr, MangledNameType wantQualifiedNames)
293 {
294     const char [] demangleFunctionOrDelegate = demangleExtern!(( str[0] ))
295         ~ demangleReturnValue!(str[1..$], wantQualifiedNames)
296         ~ " " ~ funcOrDelegStr ~ "("
297         ~ demangleParamList!(str[1..1+demangleParamListAndRetValConsumed!(str[1..$])], wantQualifiedNames)
298         ~ ")";
299 }
300
301 // Special case: types that are in function parameters
302 // For function parameters, the type can also contain 'lazy', 'out' or 'inout'.
303 template demangleFunctionParamType(string str, MangledNameType wantQualifiedNames)
304 {
305     static if (str[0]=='L')
306         const char [] demangleFunctionParamType = "lazy " ~ demangleType!(str[1..$], wantQualifiedNames);
307     else static if (str[0]=='K')
308         const char [] demangleFunctionParamType = "inout " ~ demangleType!(str[1..$], wantQualifiedNames);
309     else static if (str[0]=='J')
310         const char [] demangleFunctionParamType = "out " ~ demangleType!(str[1..$], wantQualifiedNames);
311     else const char [] demangleFunctionParamType = demangleType!(str, wantQualifiedNames);
312 }
313
314 // Deal with 'out' and 'inout' parameters
315 template demangleFunctionParamTypeConsumed(string str)
316 {
317     static if (str[0]=='K' || str[0]=='J' || str[0]=='L')
318         const int demangleFunctionParamTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
319     else const int demangleFunctionParamTypeConsumed = demangleTypeConsumed!(str);
320 }
321
322 // Return true if c indicates a function. As well as 'F', it can be extern(Pascal), (C), (C++) or (Windows).
323 template isMangledFunction(char c)
324 {
325     const bool isMangledFunction = (c=='F' || c=='U' || c=='W' || c=='V' || c=='R');
326 }
327
328 template demangleExtern(char c)
329 {
330     static if (c=='F') const char [] demangleExtern = "";
331     else static if (c=='U') const char [] demangleExtern = "extern (C) ";
332     else static if (c=='W') const char [] demangleExtern = "extern (Windows) ";
333     else static if (c=='V') const char [] demangleExtern = "extern (Pascal) ";
334     else static if (c=='R') const char [] demangleExtern = "extern (C++) ";
335     else static assert(0, "Unrecognized extern function.");
336 }
337
338 // Skip through the string until we find the return value. It can either be Z for normal
339 // functions, or Y for vararg functions.
340 template demangleReturnValue(string str, MangledNameType wantQualifiedNames)
341 {
342     static assert(str.length>=1, "Demangle error(Function): No return value found");
343     static if (str[0]=='Z' || str[0]=='Y' || str[0]=='X')
344         const char[] demangleReturnValue = demangleType!(str[1..$], wantQualifiedNames);
345     else const char [] demangleReturnValue = demangleReturnValue!(str[demangleFunctionParamTypeConsumed!(str)..$], wantQualifiedNames);
346 }
347
348 // Stop when we get to the return value
349 template demangleParamList(string str, MangledNameType wantQualifiedNames, string commastr = "")
350 {
351     static if (str[0] == 'Z')
352         const char [] demangleParamList = "";
353     else static if (str[0] == 'Y')
354         const char [] demangleParamList = commastr ~ "...";
355     else static if (str[0]=='X') // lazy ...
356         const char[] demangleParamList = commastr ~ "...";
357     else
358         const char [] demangleParamList =  commastr ~
359             demangleFunctionParamType!(str[0..demangleFunctionParamTypeConsumed!(str)], wantQualifiedNames)
360             ~ demangleParamList!(str[demangleFunctionParamTypeConsumed!(str)..$], wantQualifiedNames, ", ");
361 }
362
363 // How many characters are used in the parameter list and return value
364 template demangleParamListAndRetValConsumed(string str)
365 {
366     static assert (str.length>0, "Demangle error(ParamList): No return value found");
367     static if (str[0]=='Z' || str[0]=='Y' || str[0]=='X')
368         const int demangleParamListAndRetValConsumed = 1 + demangleTypeConsumed!(str[1..$]);
369     else {
370         const int demangleParamListAndRetValConsumed = demangleFunctionParamTypeConsumed!(str)
371             + demangleParamListAndRetValConsumed!(str[demangleFunctionParamTypeConsumed!(str)..$]);
372     }
373 }
374
375 // --------------------------------------------
376 //              TEMPLATES
377
378 template templateValueArgConsumed(string str)
379 {
380     static if (str[0]=='n') const int templateValueArgConsumed = 1;
381     else static if (beginsWithDigit!(str)) const int templateValueArgConsumed = countLeadingDigits!(str);
382     else static if (str[0]=='N') const int templateValueArgConsumed = 1 + countLeadingDigits!(str[1..$]);
383     else static if (str[0]=='e') const int templateValueArgConsumed = 1 + 20;
384     else static if (str[0]=='c') const int templateValueArgConsumed = 1 + 40;
385     else static assert(0, "Unknown character in template value argument");
386 }
387
388 // pretty-print a template value argument.
389 template prettyValueArg(string str)
390 {
391     static if (str[0]=='n') const char [] prettyValueArg = "null";
392     else static if (beginsWithDigit!(str)) const char [] prettyValueArg = str;
393     else static if ( str[0]=='N') const char [] prettyValueArg = "-" ~ str[1..$];
394     else static if ( str[0]=='e') const char [] prettyValueArg = "0x" ~ str[1..$];
395     else static if ( str[0]=='c') const char [] prettyValueArg = "0x" ~ str[1..22] ~ " + 0x" ~ str[21..41] ~ "i";
396     else const char [] prettyValueArg = "Value arg {" ~ str[0..$] ~ "}";
397 }
398
399 // Pretty-print a template argument
400 template prettyTemplateArg(string str, MangledNameType wantQualifiedNames)
401 {
402     static if (str[0]=='S') // symbol name
403         const char [] prettyTemplateArg = prettyLname!(str[1..$], wantQualifiedNames);
404     else static if (str[0]=='V') // value
405         const char [] prettyTemplateArg =
406             demangleType!(str[1..1+demangleTypeConsumed!(str[1..$])], wantQualifiedNames)
407             ~ " = " ~ prettyValueArg!(str[1+demangleTypeConsumed!(str[1..$])..$]);
408     else static if (str[0]==