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 enum string,
14  and a 'Consumed' metafunction, which returns an integer, the number of characters which
15  are used.
16 */
17 import std.algorithm;
18 
19 /*****************************************
20  * How should the name be displayed?
21  */
22 enum MangledNameType
23 {
24     PrettyName,    // With full type information
25     QualifiedName, // No type information, just identifiers seperated by dots
26     SymbolName     // Only the ultimate identifier
27 }
28 
29 /*****************************************
30  * Pretty-prints a mangled type string.
31  */
32 template demangleType(string str, MangledNameType wantQualifiedNames = MangledNameType.PrettyName)
33 {
34     static if (wantQualifiedNames != MangledNameType.PrettyName) {
35         // There are only a few types where symbolnameof!(), qualifiednameof!()
36         // make sense.
37         static if (str[0]=='C' || str[0]=='S' || str[0]=='E' || str[0]=='T')
38             enum string demangleType = prettyLname!(str[1..$], wantQualifiedNames);
39         else {
40             static assert(0, "Demangle error: type '" ~ str ~ "' does not contain a qualified name");
41         }
42     } else static if (str[0] == 'A') // dynamic array
43         enum string demangleType = demangleType!(str[1..$], wantQualifiedNames) ~ "[]";
44     else static if (str[0] == 'H')   // associative array
45         enum string demangleType
46             = demangleType!(str[1+demangleTypeConsumed!(str[1..$])..$], wantQualifiedNames)
47             ~ "[" ~ demangleType!(str[1..1+(demangleTypeConsumed!(str[1..$]))], wantQualifiedNames) ~ "]";
48     else static if (str[0] == 'G') // static array
49         enum string demangleType
50             = demangleType!(str[1+countLeadingDigits!(str[1..$])..$], wantQualifiedNames)
51             ~ "[" ~ str[1..1+countLeadingDigits!(str[1..$]) ] ~ "]";
52     else static if (str[0]=='C')
53         enum string demangleType = "class " ~ prettyLname!(str[1..$], wantQualifiedNames);
54     else static if (str[0]=='S')
55         enum string demangleType = "struct " ~ prettyLname!(str[1..$], wantQualifiedNames);
56     else static if (str[0]=='E')
57         enum string demangleType = "enum " ~ prettyLname!(str[1..$], wantQualifiedNames);
58     else static if (str[0]=='T')
59         enum string demangleType = "typedef " ~ prettyLname!(str[1..$], wantQualifiedNames);
60     else static if (str[0]=='D' && str.length>2 && isMangledFunction!(( str[1] )) ) // delegate
61         enum string demangleType = demangleFunctionOrDelegate!(str[1..$], "delegate ", wantQualifiedNames);
62     else static if (str[0]=='P' && str.length>2 && isMangledFunction!(( str[1] )) ) // function pointer
63         enum string demangleType = demangleFunctionOrDelegate!(str[1..$], "function ", wantQualifiedNames);
64     else static if (str[0]=='P') // only after we've dealt with function pointers
65         enum string demangleType = demangleType!(str[1..$], wantQualifiedNames) ~ "*";
66     else static if(str[0]=='y'){
67         enum string demangleType = "immutable(" ~ demangleType!(str[1..$], wantQualifiedNames) ~ ")";
68     }else static if(str[0]=='x'){
69         enum string demangleType = "const(" ~ demangleType!(str[1..$], wantQualifiedNames) ~ ")";
70     }else static if(str[0]=='O'){
71         enum string demangleType = "shared(" ~ demangleType!(str[1..$], wantQualifiedNames) ~ ")";
72     }else static if(str.length > 1 && str[0 .. 2]=="Ng"){
73         enum string demangleType = "inout(" ~ demangleType!(str[2..$], wantQualifiedNames) ~ ")";
74     }else static if (str[0]=='F')
75         enum string demangleType = demangleFunctionOrDelegate!(str, "", wantQualifiedNames);
76     else static if(str[0] == 'B') {
77         static assert(0, "type tuple not handled yet");
78     }else enum string demangleType = demangleBasicType!(str);
79 }
80 
81 // split these off because they're numerous and simple
82 // Note: For portability, could replace "v" with void.mangleof, etc.
83 template demangleBasicType(string str)
84 {
85          static if (str == "v") enum string demangleBasicType = "void";
86     else static if (str == "b") enum string demangleBasicType = "bool";
87     // possibly a bug in the D name mangling algorithm?
88     else static if (str == "x") enum string demangleBasicType = "bool";
89 
90     // integral types
91     else static if (str == "g") enum string demangleBasicType = "byte";
92     else static if (str == "h") enum string demangleBasicType = "ubyte";
93     else static if (str == "s") enum string demangleBasicType = "short";
94     else static if (str == "t") enum string demangleBasicType = "ushort";
95     else static if (str == "i") enum string demangleBasicType = "int";
96     else static if (str == "k") enum string demangleBasicType = "uint";
97     else static if (str == "l") enum string demangleBasicType = "long";
98     else static if (str == "m") enum string demangleBasicType = "ulong";
99     // floating point
100     else static if (str == "e") enum string demangleBasicType = "real";
101     else static if (str == "d") enum string demangleBasicType = "double";
102     else static if (str == "f") enum string demangleBasicType = "float";
103 
104     else static if (str == "j") enum string demangleBasicType = "ireal";
105     else static if (str == "p") enum string demangleBasicType = "idouble";
106     else static if (str == "o") enum string demangleBasicType = "ifloat";
107 
108     else static if (str == "c") enum string demangleBasicType = "creal";
109     else static if (str == "r") enum string demangleBasicType = "cdouble";
110     else static if (str == "q") enum string demangleBasicType = "cfloat";
111     // Char types
112     else static if (str == "a") enum string demangleBasicType = "char";
113     else static if (str == "u") enum string demangleBasicType = "wchar";
114     else static if (str == "w") enum string demangleBasicType = "dchar";
115 
116     else static assert(0, "Demangle Error: '" ~ str ~ "' is not a recognised basic type");
117 }
118 
119 template demangleTypeConsumed(string str)
120 {
121     static if (str[0]=='A')
122         enum int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
123     else static if (str[0]=='H')
124         enum int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$])
125             + demangleTypeConsumed!(str[1+demangleTypeConsumed!(str[1..$])..$]);
126     else static if (str[0]=='G')
127         enum int demangleTypeConsumed = 1 + countLeadingDigits!(str[1..$])
128             + demangleTypeConsumed!( str[1+countLeadingDigits!(str[1..$])..$] );
129     else static if (str.length>2 && (str[0]=='P' || str[0]=='D') && isMangledFunction!(( str[1] )) )
130         enum int demangleTypeConsumed = 2 + demangleParamListAndRetValConsumed!(str[2..$]);
131     else static if (str[0]=='P') // only after we've dealt with function pointers
132         enum int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
133     else static if (str[0]=='C' || str[0]=='S' || str[0]=='E' || str[0]=='T')
134         enum int demangleTypeConsumed = 1 + getQualifiedNameConsumed!(str[1..$]);
135     else static if(str[0] == 'y' && str.length>1) {
136         enum int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
137     }else static if(str[0] == 'O' && str.length>1) {
138         enum int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
139     }else static if(str[0] == 'x' && str.length>1) {
140         enum int demangleTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
141     }else static if(str.length>2 && str[0 .. 2] == "Ng" ) {
142         enum int demangleTypeConsumed = 2 + demangleTypeConsumed!(str[2..$]);
143     }else static if (str[0]=='F' && str.length>1)
144         enum int demangleTypeConsumed = 1 + demangleParamListAndRetValConsumed!(str[1..$]);
145     else // it's a Basic Type
146         enum int demangleTypeConsumed = 1;
147 }
148 
149 // --------------------------------------------
150 //              STATIC ARRAYS
151 
152 // For static arrays, count number of digits used (eg, return 3 for "674")
153 template countLeadingDigits(string str)
154 {
155     static if (str.length>0 && beginsWithDigit!( str))
156         enum int countLeadingDigits = 1 + countLeadingDigits!( str[1..$]);
157     else enum int countLeadingDigits = 0;
158 }
159 
160 // --------------------------------------------
161 //              LNAMES
162 
163 // str must start with an Lname: first chars give the length
164 // reads the digits from the front of str, gets the Lname
165 // Sometimes the characters following the length are also digits!
166 // (this happens with templates, when the name being 'lengthed' is itself an Lname).
167 // We guard against this by ensuring that the L is less than the length of the string.
168 template getLname(string str)
169 {
170     static if (str.length <= 9+1 || !beginsWithDigit!(str[1..$]) )
171         enum string getLname = str[1..(str[0]-'0' + 1)];
172     else static if (str.length <= 99+2 || !beginsWithDigit!(str[2..$]) )
173         enum string getLname = str[2..((str[0]-'0')*10 + str[1]-'0'+ 2)];
174     else static if (str.length <= 999+3 || !beginsWithDigit!(str[3..$]) )
175         enum string getLname =
176             str[3..((str[0]-'0')*100 + (str[1]-'0')*10 + str[2]-'0' + 3)];
177     else
178         enum string getLname =
179             str[4..((str[0]-'0')*1000 + (str[1]-'0')*100 + (str[2]-'0')*10 + (str[3]-'0') + 4)];
180 }
181 
182 // Deal with the case where an Lname contains an embedded "__D".
183 // This can happen when classes, typedefs, etc are declared inside a function.
184 template pretty_Dname(string str, int dotnameconsumed, MangledNameType wantQualifiedNames)
185 {
186     static if ( isMangledFunction!( (str[2+dotnameconsumed]))) {
187         enum string pretty_Dname = pretty_Dfunction!(str, dotnameconsumed,
188             demangleParamListAndRetValConsumed!(str[3+dotnameconsumed..$]), wantQualifiedNames);
189     } else {
190         static if (wantQualifiedNames == MangledNameType.PrettyName) {
191             enum string pretty_Dname =
192                 demangleType!(str[2+dotnameconsumed..$], wantQualifiedNames)
193                 ~ " " ~ getQualifiedName!(str[2..$], wantQualifiedNames);
194         } else {
195             enum string pretty_Dname = getQualifiedName!(str[2..$], wantQualifiedNames);
196         }
197     }
198 }
199 
200 // DFunction(_D7testdll3barFiZAya, dotnameconsumed=12, paramlistconsumed=4)
201 
202 // Deal with the case where an Lname contains an embedded ("__D") function.
203 // Split into a seperate function because it's so complicated.
204 template pretty_Dfunction(string str, int dotnameconsumed, int paramlistconsumed,
205     MangledNameType wantQualifiedNames)
206 {
207     static if (wantQualifiedNames == MangledNameType.PrettyName) {
208         enum string pretty_Dfunction =
209             demangleFunctionOrDelegate!(str[2 + dotnameconsumed .. 3 + dotnameconsumed + paramlistconsumed],
210                 getQualifiedName!(str[2..2+dotnameconsumed], wantQualifiedNames), wantQualifiedNames)
211                 // BUG: This shouldn't be necessary, the string length is wrong somewhere
212             ~ getQualifiedName!(str[3 + dotnameconsumed + paramlistconsumed .. $], wantQualifiedNames, ".");
213     } else static if (wantQualifiedNames == MangledNameType.QualifiedName) {
214         // Qualified name
215         enum string pretty_Dfunction = getQualifiedName!(str[2..2+dotnameconsumed], wantQualifiedNames)
216             ~ getQualifiedName!(str[3 + dotnameconsumed + paramlistconsumed .. $], wantQualifiedNames, ".");
217     } else { // symbol name
218         static if (3 + dotnameconsumed + paramlistconsumed == str.length) {
219             enum string pretty_Dfunction = getQualifiedName!(str[2..2+dotnameconsumed], wantQualifiedNames);
220         } else {
221             enum string pretty_Dfunction = getQualifiedName!(
222             str[3 + dotnameconsumed + paramlistconsumed .. $], wantQualifiedNames);
223         }
224     }
225  }
226 
227 // for an Lname that begins with "_D"
228 template get_DnameConsumed(string str)
229 {
230     enum int get_DnameConsumed = 2 + getQualifiedNameConsumed!(str[2..$])
231         + demangleTypeConsumed!(str[2+getQualifedNameConsumed!(str[2..$])..$]);
232 }
233 
234 // If Lname is a template, shows it as a template
235 template prettyLname(string str, MangledNameType wantQualifiedNames)
236 {
237     static if (str.length>3 && str[0..3] == "__T") // Template instance name
238         static if (wantQualifiedNames == MangledNameType.PrettyName) {
239             enum string prettyLname =
240                 prettyLname!(str[3..$], wantQualifiedNames) ~ "!("
241                 ~ prettyTemplateArgList!(str[3+getQualifiedNameConsumed!(str[3..$])..$], wantQualifiedNames)
242                 ~ ")";
243         } else {
244             enum string prettyLname =
245                 prettyLname!(str[3..$], wantQualifiedNames);
246         }
247     else static if (str.length>2 && str[0..2] == "_D") {
248         enum string prettyLname = pretty_Dname!(str, getQualifiedNameConsumed!(str[2..$]), wantQualifiedNames);
249     } else static if ( beginsWithDigit!( str ) )
250         enum string prettyLname = getQualifiedName!(str[0..getQualifiedNameConsumed!(str)], wantQualifiedNames);
251     else enum string prettyLname = str;
252 }
253 
254 // str must start with an lname: first chars give the length
255 // how many chars are taken up with length digits + the name itself
256 template getLnameConsumed(string str)
257 {
258     static if (str.length==0)
259         enum int getLnameConsumed=0;
260     else static if (str.length <= (9+1) || !beginsWithDigit!(str[1..$]) )
261         enum int getLnameConsumed = 1 + str[0]-'0';
262     else static if (str.length <= (99+2) || !beginsWithDigit!( str[2..$]) )
263         enum int getLnameConsumed = (str[0]-'0')*10 + str[1]-'0' + 2;
264     else static if (str.length <= (999+3) || !beginsWithDigit!( str[3..$]) )
265         enum int getLnameConsumed = (str[0]-'0')*100 + (str[1]-'0')*10 + str[2]-'0' + 3;
266     else
267         enum int getLnameConsumed = (str[0]-'0')*1000 + (str[1]-'0')*100 + (str[2]-'0')*10 + (str[3]-'0') + 4;
268 }
269 
270 template getQualifiedName(string str, MangledNameType wantQualifiedNames, string dotstr = "")
271 {
272     static if (str.length==0) enum string getQualifiedName="";
273 //    else static if (str.length>2 && str[0]=='_' && str[1]=='D')
274 //        enum string getDotName = getQualifiedName!(str[2..$], wantQualifiedNames);
275     else {
276         static assert (beginsWithDigit!(str));
277         static if ( getLnameConsumed!(str) < str.length && beginsWithDigit!(str[getLnameConsumed!(str)..$]) ) {
278             static if (wantQualifiedNames == MangledNameType.SymbolName) {
279                 // For symbol names, only display the last symbol
280                 enum string getQualifiedName =
281                     getQualifiedName!(str[getLnameConsumed!(str) .. $], wantQualifiedNames, "");
282             } else {
283                 // Qualified and pretty names display everything
284                 enum string getQualifiedName = dotstr
285                     ~ prettyLname!(getLname!(str), wantQualifiedNames)
286                     ~ getQualifiedName!(str[getLnameConsumed!(str) .. $], wantQualifiedNames, ".");
287             }
288         } else {
289             enum string getQualifiedName = dotstr ~ prettyLname!(getLname!(str), wantQualifiedNames);
290         }
291     }
292 }
293 
294 template getQualifiedNameConsumed (string str)
295 {
296     static if ( str.length>1 &&  beginsWithDigit!(str) ) {
297         static if (getLnameConsumed!(str) < str.length && beginsWithDigit!( str[getLnameConsumed!(str)..$])) {
298             enum int getQualifiedNameConsumed = getLnameConsumed!(str)
299                 + getQualifiedNameConsumed!(str[getLnameConsumed!(str) .. $]);
300         } else {
301             enum int getQualifiedNameConsumed = getLnameConsumed!(str);
302         }
303     } /*else static if (str.length>1 && str[0]=='_' && str[1]=='D') {
304         enum int getQualifiedNameConsumed = get_DnameConsumed!(str)
305             + getQualifiedNameConsumed!(str[1+get_DnameConsumed!(str)..$]);
306     }*/ else static assert(0);
307 }
308 
309 // ----------------------------------------
310 //              FUNCTIONS
311 
312 /* str[0] must indicate the extern linkage of the function. funcOrDelegStr is the name of the function,
313 * or "function " or "delegate "
314 */
315 template demangleFunctionOrDelegate(string str, string funcOrDelegStr, MangledNameType wantQualifiedNames)
316 {
317     enum fe = funcAttrsConsumed!(str[1 .. $]);
318     enum string funcRest = str[1+fe..$];
319     enum e = demangleParamListAndRetValConsumed!(funcRest);
320     enum string funcAttrs = demangleFuncAttrs!(str[1 .. 1+fe], wantQualifiedNames);
321     enum string demangleFunctionOrDelegate = funcAttrs ~ demangleExtern!(( str[0] ))
322         ~ demangleReturnValue!(funcRest, wantQualifiedNames)
323         ~ " " ~ funcOrDelegStr ~ "("
324         ~ demangleParamList!(funcRest[0..demangleParamListAndRetValConsumed!(funcRest)], wantQualifiedNames)
325         ~ ")";
326 }
327 
328 template demangleFuncAttrs(string str, MangledNameType wantQualifiedNames) {
329     static if(str.startsWith("Na")) {
330         enum string demangleFuncAttrs = "pure " ~ demangleFuncAttrs!(str[2..$], wantQualifiedNames);
331     }else static if(str.startsWith("Nb")) {
332         enum string demangleFuncAttrs = "nothrow " ~ demangleFuncAttrs!(str[2..$], wantQualifiedNames);
333     }else static if(str.startsWith("Nc")) {
334         enum string demangleFuncAttrs = "ref " ~ demangleFuncAttrs!(str[2..$], wantQualifiedNames);
335     }else static if(str.startsWith("Nd")) {
336         enum string demangleFuncAttrs = "@property " ~ demangleFuncAttrs!(str[2..$], wantQualifiedNames);
337     }else static if(str.startsWith("Ne")) {
338         enum string demangleFuncAttrs = "@trusted " ~ demangleFuncAttrs!(str[2..$], wantQualifiedNames);
339     }else static if(str.startsWith("Nf")) {
340         enum string demangleFuncAttrs = "@safe " ~ demangleFuncAttrs!(str[2..$], wantQualifiedNames);
341     }else{
342         enum string demangleFuncAttrs = "";
343     }
344 }
345 
346 template funcAttrsConsumed(string str) {
347     static if(str.length > 1 && str[0] == 'N' && 'a' <= str[1] && str[1] <= 'f') {
348         enum int funcAttrsConsumed = 2 + funcAttrsConsumed!(str[2..$]);
349     }else {
350         enum int funcAttrsConsumed = 0;
351     }
352 }
353 
354 // Special case: types that are in function parameters
355 // For function parameters, the type can also contain 'lazy', 'out' or 'ref'.
356 template demangleFunctionParamType(string str, MangledNameType wantQualifiedNames)
357 {
358     static if (str[0]=='L')
359         enum string demangleFunctionParamType = "lazy " ~ demangleType!(str[1..$], wantQualifiedNames);
360     else static if (str[0]=='K')
361         enum string demangleFunctionParamType = "ref " ~ demangleType!(str[1..$], wantQualifiedNames);
362     else static if (str[0]=='J')
363         enum string demangleFunctionParamType = "out " ~ demangleType!(str[1..$], wantQualifiedNames);
364     else enum string demangleFunctionParamType = demangleType!(str, wantQualifiedNames);
365 }
366 
367 // Deal with 'out' and 'ref' parameters
368 template demangleFunctionParamTypeConsumed(string str)
369 {
370     static if (str[0]=='K' || str[0]=='J' || str[0]=='L')
371         enum int demangleFunctionParamTypeConsumed = 1 + demangleTypeConsumed!(str[1..$]);
372     else enum int demangleFunctionParamTypeConsumed = demangleTypeConsumed!(str);
373 }
374 
375 // Return true if c indicates a function. As well as 'F', it can be extern(Pascal), (C), (C++) or (Windows).
376 template isMangledFunction(char c)
377 {
378     enum bool isMangledFunction = (c=='F' || c=='U' || c=='W' || c=='V' || c=='R');
379 }
380 
381 template demangleExtern(char c)
382 {
383     static if (c=='F') enum string demangleExtern = "";
384     else static if (c=='U') enum string demangleExtern = "extern (C) ";
385     else static if (c=='W') enum string demangleExtern = "extern (Windows) ";
386     else static if (c=='V') enum string demangleExtern = "extern (Pascal) ";
387     else static if (c=='R') enum string demangleExtern = "extern (C++) ";
388     else static assert(0, "Unrecognized extern function.");
389 }
390 
391 // Skip through the string until we find the return value. It can either be Z for normal
392 // functions, or Y for vararg functions.
393 template demangleReturnValue(string str, MangledNameType wantQualifiedNames)
394 {
395     static assert(str.length>=1, "Demangle error(Function): No return value found");
396     static if (str[0]=='Z' || str[0]=='Y' || str[0]=='X')
397         enum string demangleReturnValue = demangleType!(str[1..$], wantQualifiedNames);
398     else enum string demangleReturnValue = demangleReturnValue!(str[demangleFunctionParamTypeConsumed!(str)..$], wantQualifiedNames);
399 }
400 
401 // Stop when we get to the return value
402 template demangleParamList(string str, MangledNameType wantQualifiedNames, string commastr = "")
403 {
404     static if (str[0] == 'Z')
405         enum string demangleParamList = "";
406     else static if (str[0] == 'Y')
407         enum string demangleParamList = commastr ~ "...";
408     else static if (str[0]=='X') // lazy ...
409         enum string demangleParamList = commastr ~ "...";
410     else
411         enum string demangleParamList =  commastr ~
412             demangleFunctionParamType!(str[0..demangleFunctionParamTypeConsumed!(str)], wantQualifiedNames)
413             ~ demangleParamList!(str[demangleFunctionParamTypeConsumed!(str)..$], wantQualifiedNames, ", ");
414 }
415 
416 // How many characters are used in the parameter list and return value
417 template demangleParamListAndRetValConsumed(string str)
418 {
419     static assert (str.length>0, "Demangle error(ParamList): No return value found");
420     static if (str[0]=='Z' || str[0]=='Y' || str[0]=='X')
421         enum int demangleParamListAndRetValConsumed = 1 + demangleTypeConsumed!(str[1..$]);
422     else {
423         enum int demangleParamListAndRetValConsumed = demangleFunctionParamTypeConsumed!(str)
424             + demangleParamListAndRetValConsumed!(str[demangleFunctionParamTypeConsumed!(str)..$]);
425     }
426 }
427 
428 // --------------------------------------------
429 //              TEMPLATES
430 
431 template templateValueArgConsumed(string str)
432 {
433     static if (str[0]=='n') enum int templateValueArgConsumed = 1;
434     else static if (beginsWithDigit!(str)) enum int templateValueArgConsumed = countLeadingDigits!(str);
435     else static if (str[0]=='N') enum int templateValueArgConsumed = 1 + countLeadingDigits!(str[1..$]);
436     else static if (str[0]=='e') enum int templateValueArgConsumed = 1 + hexFloatConsumed!(str[1..$]);
437     else static if (str[0]=='c') {
438         enum int i1 = 1 + hexFloatConsumed!(str[1 .. $]);
439         enum int templateValueArgConsumed = i1 + 1 + hexFloatConsumed!(str[i1+1 .. $]);
440     }
441     else static assert(0, "Unknown character in template value argument");
442 }
443 
444 template hexFloatConsumed(string str) {
445     static if(str.startsWith("NAN") || str[1 .. $].startsWith("INF")) {
446         enum int hexFloatConsumed = 3;
447     }else static if(str.startsWith("NINF")) {
448         enum int hexFloatConsumed = 4;
449     }else{ 
450         static if(str.startsWith("N")) {
451             enum hx_c = 1;
452         }else {
453             enum hx_c = 0;
454         }
455         enum string hxbase = str[hx_c .. $];
456         enum hx_c1 = countUntil!("!(std.ascii.isHexDigit(a) && (std.ascii.isDigit(a) || std.ascii.isUpper(a)))")(hxbase);
457         static if(hx_c1 < hxbase.length && hxbase[hx_c1] == 'P') {
458             enum string hxexp = hxbase[hx_c1+1 .. $];
459             static if(hxexp.startsWith("N")) {
460                 enum hx_c2 = 1;
461             }else {
462                 enum hx_c2 = 0;
463             }
464             enum string hxexp2 = hxexp[hx_c2 .. $];
465             enum hx_c3 = countUntil!("!std.ascii.isDigit(a)")(hxexp2);
466             enum int hexFloatConsumed = hx_c + hx_c1 + hx_c2 + hx_c3 + 1;
467         }else {
468             enum int hexFloatConsumed = hx_c + hx_c1;
469         }
470 
471     }
472 }
473 
474 // pretty-print a template value argument.
475 template prettyValueArg(string str)
476 {
477     static if (str[0]=='n') enum string prettyValueArg = "null";
478     else static if (beginsWithDigit!(str)) enum string prettyValueArg = str;
479     else static if ( str[0]=='N') enum string prettyValueArg = "-" ~ str[1..$];
480     else static if ( str[0]=='e') enum string prettyValueArg = "0x" ~ str[1..$];
481     else static if ( str[0]=='c') {
482         enum g = findSplit(str[1 .. $], "c");
483         enum string prettyValueArg = "0x" ~ g[0] ~ " + 0x" ~ g[2] ~ "i";
484     } else enum string prettyValueArg = "Value arg {" ~ str[0..$] ~ "}";
485 }
486 
487 // Pretty-print a template argument
488 template prettyTemplateArg(string str, MangledNameType wantQualifiedNames)
489 {
490     static if (str[0]=='S') // symbol name
491         enum string prettyTemplateArg = prettyLname!(str[1..$], wantQualifiedNames);
492     else static if (str[0]=='V') // value
493         enum string prettyTemplateArg =
494             demangleType!(str[1..1+demangleTypeConsumed!(str[1..$])], wantQualifiedNames)
495             ~ " = " ~ prettyValueArg!(str[1+demangleTypeConsumed!(str[1..$])..$]);
496     else static if (str[0]=='T') // type
497         enum string prettyTemplateArg = demangleType!(str[1..$], wantQualifiedNames);
498     else static assert(0, "Unrecognised template argument type: {" ~ str ~ "}");
499 }
500 
501 template templateArgConsumed(string str)
502 {
503     static if (str[0]=='S') // symbol name
504         enum int templateArgConsumed = 1 + getLnameConsumed!(str[1..$]);
505     else static if (str[0]=='V') // value
506     {
507         enum e = 1 + demangleTypeConsumed!(str[1..$]);
508         enum int templateArgConsumed = 1 + demangleTypeConsumed!(str[1..$]) +
509             templateValueArgConsumed!(str[1+demangleTypeConsumed!(str[1..$])..$]);
510     }
511     else static if (str[0]=='T') // type
512         enum int templateArgConsumed = 1 + demangleTypeConsumed!(str[1..$]);
513     else static assert(0, "Unrecognised template argument type: {" ~ str ~ "}");
514 }
515 
516 // Like function parameter lists, template parameter lists also end in a Z,
517 // but they don't have a return value at the end.
518 template prettyTemplateArgList(string str, MangledNameType wantQualifiedNames, string commastr="")
519 {
520     static if (str[0]=='Z')
521         enum string prettyTemplateArgList = "";
522     else
523        enum string prettyTemplateArgList = commastr
524             ~ prettyTemplateArg!(str[0..templateArgConsumed!(str)], wantQualifiedNames)
525             ~ prettyTemplateArgList!(str[templateArgConsumed!(str)..$], wantQualifiedNames, ", ");
526 }
527 
528 template templateArgListConsumed(string str)
529 {
530     static assert(str.length>0, "No Z found at end of template argument list");
531     static if (str[0]=='Z')
532         enum int templateArgListConsumed = 1;
533     else
534         enum int templateArgListConsumed = templateArgConsumed!(str)
535             + templateArgListConsumed!(str[templateArgConsumed!(str)..$]);
536 }
537 
538 private {
539   /*
540    * Return true if the string begins with a decimal digit
541    *
542    * beginsWithDigit!(s) is equivalent to isdigit!((s[0]));
543    * it allows us to avoid the ugly double parentheses.
544    */
545 template beginsWithDigit(string s)
546 {
547   static if (s[0]>='0' && s[0]<='9')
548     enum bool beginsWithDigit = true;
549   else enum bool beginsWithDigit = false;
550 }
551 }
552 
553 
554 
555 // --------------------------------------------
556 //              UNIT TESTS
557 
558 debug(UnitTest){
559 
560 private {
561 
562 enum string THISFILE = "meta.Demangle";
563 
564 ireal SomeFunc(ushort u) { return -3i; }
565 idouble SomeFunc2(ref ushort u, ubyte w) { return -3i; }
566 byte[] SomeFunc3(out dchar d, ...) { return null; }
567 ifloat SomeFunc4(lazy void[] x...) { return 2i; }
568 char[dchar] SomeFunc5(lazy int delegate()[] z...);
569 
570 extern (Windows) {
571     alias void function (double, long) WinFunc;
572 }
573 
574 import core.vararg;
575 extern (Pascal) {
576     alias short[wchar] delegate (bool, ...) PascFunc;
577 }
578 extern (C) {
579     alias dchar delegate () CFunc;
580 }
581 extern (C++) {
582     alias cfloat function (wchar) CPPFunc;
583 }
584 
585 inout(int) inoutFunc(inout int i) {
586     return i+1;
587 }
588 
589 int pureFunc(int i) pure {
590     return i+1;
591 }
592 int purenothrowFunc(int i) pure nothrow {
593     return i+1;
594 }
595 int trustedFunc(int i) @trusted {
596     return i+1;
597 }
598 int safeFunc(int i) @safe {
599     return i+1;
600 }
601 ref int refFunc(int i) {
602     return i+1;
603 }
604 
605 interface SomeInterface {}
606 
607 static assert( demangleType!((&SomeFunc).mangleof) == "ireal function (ushort)" );
608 static assert( demangleType!((&SomeFunc2).mangleof) == "idouble function (ref ushort, ubyte)");
609 static assert( demangleType!((&SomeFunc3).mangleof) == "byte[] function (out dchar, ...)");
610 static assert( demangleType!((&SomeFunc4).mangleof) == "ifloat function (lazy void[], ...)");
611 static assert( demangleType!((&SomeFunc5).mangleof) == "char[dchar] function (lazy int delegate ()[], ...)");
612 static assert( demangleType!((WinFunc).mangleof)== "extern (Windows) void function (double, long)");
613 static assert( demangleType!((PascFunc).mangleof) == "extern (Pascal) short[wchar] delegate (bool, ...)");
614 static assert( demangleType!((CFunc).mangleof) == "extern (C) dchar delegate ()");
615 static assert( demangleType!((CPPFunc).mangleof) == "extern (C++) cfloat function (wchar)");
616 static assert(demangleType!((&inoutFunc).mangleof) == "inout(int) function (inout(int))");
617 static assert(demangleType!((&pureFunc).mangleof) == "pure int function (int)");
618 static assert(demangleType!((&purenothrowFunc).mangleof) == "pure nothrow int function (int)");
619 static assert(demangleType!((&trustedFunc).mangleof) == "@trusted int function (int)");
620 static assert(demangleType!((&safeFunc).mangleof) == "@safe int function (int)");
621 static assert(demangleType!((&refFunc).mangleof) == "ref int function (int)");
622 // Interfaces are mangled as classes
623 static assert( demangleType!(SomeInterface.mangleof) == "class " ~ THISFILE ~ ".SomeInterface");
624 
625 template ComplexTemplate(real a, creal b)
626 {
627     class ComplexTemplate {}
628 }
629 
630 int ComplexFunction(real a, creal b)(int i) {
631     return i+1;
632 }
633 
634 //static assert( demangleType!((ComplexTemplate!(1.23, 4.56+3.2i)).mangleof) == "class " ~ THISFILE ~ ".ComplexTemplate!(double = 0xa4703d0ad7a3709dff3f, cdouble = 0x85eb51b81e85eb910140c + 0xcdcccccccccccccc0040i).ComplexTemplate");
635 
636 }
637 }