1 /**
2  *   D symbol name demangling
3  *
4  *   Attempts to demangle D symbols generated by the DMD frontend.
5  *   (Which is not always technically possible.)
6  *
7  *  A sample program demangling the names passed as arguments.
8  *  ---
9  *   module demangle;
10  *   import tango.core.tools.Demangler;
11  *   import tango.io.Stdout;
12  *
13  *   void usage(){
14  *       Stdout("demangle [--help] [--level 0-9] mangledName1 [mangledName2...]").newline;
15  *   }
16  *
17  *   int main(const(char)[][]args){
18  *       uint start=1;
19  *       if (args.length>1) {
20  *           if (args[start]=="--help"){
21  *               usage();
22  *               ++start;
23  *           }
24  *           if (args[start]=="--level"){
25  *               ++start;
26  *               if (args.length==start || args[start].length!=1 || args[start][0]<'0' || 
27  *                   args[start][0]>'9') {
28  *                   Stdout("invalid level '")((args.length==start)?"*missing*":args[start])
29  *                       ("' (must be 0-9)").newline;
30  *                   usage();
31  *                   return 2;
32  *               }
33  *               demangler.verbosity=args[start+1][0]-'0';
34  *               ++start;
35  *           }
36  *       } else {
37  *           usage();
38  *           return 0;
39  *       }
40  *       foreach (n;args[start..$]){
41  *           Stdout(demangler.demangle(n)).newline;
42  *       }
43  *       return 0;
44  *   }
45  *  ---
46  *  Copyright: Copyright (C) 2007-2008 Zygfryd (aka Hxal), Fawzi. All rights reserved.
47  *  License:   tango license, apache 2.0
48  *  Authors:   Zygfryd (aka Hxal), Fawzi
49  *
50  */
51 
52 module tango.core.tools.Demangler;
53 
54 version(TangoDemangler)
55 {
56 import tango.core.Traits: ctfe_i2a;
57 import tango.stdc.string: memmove,memcpy;
58 
59 debug(traceDemangler) import tango.io.Stdout;
60 
61 
62 
63 version(DigitalMars) version(Windows) {
64     bool isMD5Hashed(const(char)[] name) {
65         if (name.length < 34 || (name.length >= 2 && name[0..2] != "_D")) {
66             return false;
67         }
68         
69         foreach (c; name[$-32..$]) {
70             if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
71                 return false;
72             }
73         }
74         
75         return true;
76     }
77     
78 
79     const(char)[] decompressOMFSymbol(const(char)[] garbled, char[]* buf) {
80         int ungarbledLength = 0;
81         bool compressed = false;
82 
83         for (int ci = 0; ci < garbled.length; ++ci) {
84             char c = garbled[ci];
85             if (0 == (c & 0x80)) {
86                 ++ungarbledLength;
87             } else {
88                 compressed = true;
89                 int matchLen = void;
90 
91                 if (c & 0x40) {
92                     matchLen = (c & 0b111) + 1;
93                 } else {
94                     if (ci+2 >= garbled.length) {
95                         return garbled;
96                     } else {
97                         matchLen = cast(int)(c & 0x38) << 4;
98                         matchLen += garbled[ci+1] & ~0x80;
99                         ci += 2;
100                     }
101                 }
102 
103                 ungarbledLength += matchLen;
104             }
105         }
106 
107         if (!compressed || ungarbledLength > (*buf).length) {
108             return garbled;
109         } else {
110             char[] ungarbled = (*buf)[$-ungarbledLength..$];
111             *buf = (*buf)[0..$-ungarbledLength];
112             int ui = 0;
113 
114             for (int ci = 0; ci < garbled.length; ++ci) {
115                 char c = garbled[ci];
116                 if (0 == (c & 0x80)) {
117                     ungarbled[ui++] = c;
118                 } else {
119                     int matchOff = void;
120                     int matchLen = void;
121 
122                     if (c & 0x40) {
123                         matchOff = ((c >> 3) & 0b111) + 1;
124                         matchLen = (c & 0b111) + 1;
125                     } else {
126                         matchOff = cast(int)(c & 0b111) << 7;
127                         matchLen = cast(int)(c & 0x38) << 4;
128                         matchLen += garbled[ci+1] & ~0x80;
129                         matchOff += garbled[ci+2] & ~0x80;
130                         ci += 2;
131                     }
132 
133                     int matchStart = ui - matchOff;
134                     if (matchStart + matchLen > ui) {
135                         // fail
136                         return garbled;
137                     }
138 
139                     const(char)[] match = ungarbled[matchStart .. matchStart + matchLen];
140                     ungarbled[ui .. ui+matchLen] = match;
141                     ui += matchLen;
142                 }
143             }
144 
145             return ungarbled;
146         }
147     }
148 }
149 
150 
151 /// decompresses a symbol and returns the full symbol, and possibly a reduced buffer space
152 /// (does something only on windows with DMD.)
153 const(char)[] decompressSymbol(const(char)[] func,char[] *buf){
154     version(DigitalMars) version(Windows){
155         if (isMD5Hashed(func)) {
156             func = func[0..$-32];
157         }
158         func = decompressOMFSymbol(func, buf);
159     }
160     return func;
161 }
162 
163 uint toUint(const(char)[] s){
164     uint res=0;
165     for (int i=0;i<s.length;++i){
166         if (s[i]>='0'&& s[i]<='9'){
167             res*=10;
168             res+=s[i]-'0';
169         } else {
170             assert(false);
171         }
172     }
173     return res;
174 }
175 
176 /**
177  *   Flexible demangler.
178  *   Attempts to demangle D symbols generated by the DMD frontend.
179  *   (Which is not always technically possible.)
180  */
181 public class Demangler
182 {
183     /** How deeply to recurse printing template parameters,
184       * for depths greater than this, an ellipsis is used. */
185     uint templateExpansionDepth = 1;
186 
187     /** Skip default members of templates (sole members named after
188       * the template.) */
189     bool foldDefaults = false;
190 
191     /** Print types of functions being part of the main symbol. */
192     bool expandFunctionTypes = false;
193 
194     /** For composite types, print the kind (class|struct|etc.) of the type. */
195     bool printTypeKind = false;
196 
197     /** Sets the verbosity level of the demangler (template expansion level,...) */
198     public void verbosity (uint level)
199     {
200         switch (level)
201         {
202             case 0:
203                 templateExpansionDepth = 0;
204                 expandFunctionTypes = false;
205                 printTypeKind = false;
206                 break;
207 
208             case 1:
209                 templateExpansionDepth = 1;
210                 expandFunctionTypes = false;
211                 printTypeKind = false;
212                 break;
213 
214             case 2:
215                 templateExpansionDepth = 1;
216                 expandFunctionTypes = false;
217                 printTypeKind = true;
218                 break;
219 
220             case 3:
221                 templateExpansionDepth = 1;
222                 expandFunctionTypes = true;
223                 printTypeKind = true;
224                 break;
225 
226             default:
227                 templateExpansionDepth = level - 2;
228                 expandFunctionTypes = true;
229                 printTypeKind = true;
230         }
231     }
232 
233     /** Creates a demangler. */
234     this ()
235     {
236         verbosity (1);
237     }
238 
239     /** Creates a demangler with the given verbosity level. */
240     this (uint verbosityLevel)
241     {
242         verbosity (verbosityLevel);
243     }
244     
245     /** Demangles the given string. */
246     public inout(char)[] demangle (inout(char)[] input)
247     {
248         // Special case for "_Dmain"
249         if (input == "_Dmain")
250             return "main";
251         char[4096] buf=void;
252         auto res=DemangleInstance(this,input,buf);
253         if (res.mangledName() && res.input.length==0){
254             return cast(inout(char)[])res.slice().dup;
255         } else {
256             if (res.slice().length) res.output.append(" ");
257             if (res.type() && res.input.length==0){
258                 return cast(inout(char)[])res.slice().dup;
259             } else {
260                 return input;
261             }
262         }
263     }
264 
265     /** Demangles the given string using output to hold the result. */
266     public inout(char)[] demangle (inout(char)[] input, char[] output)
267     {
268         // Special case for "_Dmain"
269         if (input == "_Dmain")
270             return "main";
271         auto res=DemangleInstance(this,input,output);
272         if (res.mangledName () && res.input.length==0) {
273             return cast(inout(char)[])res.slice();
274         } else {
275             if (res.slice().length) res.output.append(" ");
276             if (res.type() && res.input.length==0) {
277                 return cast(inout(char)[])res.slice();
278             } else {
279                 return input;
280             }
281         }
282     }
283 
284     /// This represents a single demangling request, and is the place where the real work is done
285     /// some more cleanup would probably be in order (maybe remove Buffer.)
286     struct DemangleInstance{
287         debug(traceDemangler) private const(char)[][] _trace;
288         private const(char)[] input;
289         private uint _templateDepth;
290         Buffer output;
291         Demangler prefs;
292         
293         struct BufState{
294             DemangleInstance* dem;
295             const(char)[] input;
296             size_t len;
297             static BufState opCall(ref DemangleInstance dem){
298                 BufState res;
299                 res.dem=&dem;
300                 res.len=dem.output.length;
301                 res.input=dem.input;
302                 return res;
303             }
304             // resets input and output buffers and returns false
305             bool reset(){
306                 dem.output.length=len;
307                 dem.input=input;
308                 return false;
309             }
310             // resets only the output buffer and returns false
311             bool resetOutput(){
312                 dem.output.length=len;
313                 return false;
314             }
315             const(char)[] sliceFrom(){
316                 return dem.output.data[len..dem.output.length];
317             }
318         }
319         
320         BufState checkpoint(){
321             return BufState(this);
322         }
323 
324         static DemangleInstance opCall(Demangler prefs,const(char)[] input,const(char)[] output){
325              char[] buff = new char[output.length];
326 					input = decompressSymbol(input, &buff);
327 					output = buff;
328             DemangleInstance res;
329             res.prefs=prefs;
330             res.input=input;
331             res._templateDepth=0;
332             res.output.data=buff;
333             debug(traceDemangler) res._trace=null;
334             return res;
335         }
336 
337         debug (traceDemangler)
338         {
339             private void trace (const(char)[] where)
340             {
341                 if (_trace.length > 500)
342                     throw new Exception ("Infinite recursion");
343                 
344                 int len=_trace.length;
345                 const(char)[] spaces = "            ";
346                 spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)];
347                 if (input.length < 50)
348                     Stdout.formatln ("{}{} : {{{}}", spaces, where, input);
349                 else
350                     Stdout.formatln ("{}{} : {{{}}", spaces, where, input[0..50]);
351                 _trace ~= where;
352             }
353 
354             private void report (T...) (const(char)[] fmt, T args)
355             {
356                 int len=_trace.length;
357                 const(char)[] spaces = "            ";
358                 spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)];
359                 Stdout (spaces);
360                 Stdout.formatln (fmt, args);
361             }
362 
363             private void trace (bool result)
364             {
365                 //auto tmp = _trace[$-1];
366                 _trace = _trace[0..$-1];
367                 int len=_trace.length;
368                 const(char)[] spaces = "            ";
369                 spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)];
370                 Stdout(spaces);
371                 if (!result)
372                     Stdout.formatln ("fail");
373                 else
374                     Stdout.formatln ("success");
375             }
376         }
377 
378         const(char)[] slice(){
379             return output.slice();
380         }
381 
382         private const(char)[] consume (uint amt)
383         {
384             const(char)[] tmp = input[0 .. amt];
385             input = input[amt .. $];
386             return tmp;
387         }
388 
389         bool mangledName ()
390         out (result)
391         {
392             debug(traceDemangler) trace (result);
393         }
394         body
395         {
396             debug(traceDemangler) trace ("mangledName");
397             
398             if (input.length<2)
399                 return false;
400             if (input[0]=='D'){
401                 consume(1);
402             } else if (input[0..2] == "_D") {
403                 consume(2);
404             } else {
405                 return false;
406             }
407 
408             if (! typedqualifiedName ())
409                 return false;
410 
411             if (input.length > 0) {
412                 auto pos1=checkpoint();
413                 output.append("<");
414                 if (! type ())
415                     pos1.reset(); // return false??
416                 else if (prefs.printTypeKind){
417                     output.append(">");
418                 } else {
419                     pos1.resetOutput();
420                 }
421             }
422 
423             //Stdout.formatln ("mangledName={}", namebuf.slice);
424 
425             return true;
426         }
427 
428         bool typedqualifiedName ()
429         out (result)
430         {
431             debug(traceDemangler) trace (result);
432         }
433         body
434         {
435             debug(traceDemangler) trace ("typedqualifiedName");
436 
437             auto posCar=checkpoint();
438             if (! symbolName ())
439                 return false;
440             const(char)[] car=posCar.sliceFrom();
441             
442             // undocumented
443             auto pos=checkpoint();
444             output.append ("{");
445             if (typeFunction ()){
446                 if (!prefs. expandFunctionTypes){
447                     pos.resetOutput();
448                 } else {
449                     output.append ("}");
450                 }
451             } else {
452                 pos.reset();
453             }
454 
455             pos=checkpoint();
456             output.append (".");
457             if (typedqualifiedName ())
458             {
459                 if (prefs.foldDefaults && car.length<pos.sliceFrom().length &&
460                     car==pos.sliceFrom()[1..car.length+1]){
461                     memmove(&output.data[posCar.len],&output.data[pos.len+1],output.length-pos.len);
462                     output.length+=posCar.len-pos.len-1;
463                 }
464             } else {
465                 pos.reset();
466             }
467 
468             return true;
469         }
470 
471         bool qualifiedName (bool aliasHack = false)
472         out (result)
473         {
474             debug(traceDemangler) trace (result);
475         }
476         body
477         {
478             debug(traceDemangler) trace (aliasHack ? "qualifiedNameAH" : "qualifiedName");
479 
480             auto pos=checkpoint();
481             if (! symbolName (aliasHack))
482                 return false;
483             const(char)[] car=pos.sliceFrom();
484 
485             auto pos1=checkpoint();
486             output.append (".");
487             if (typedqualifiedName ())
488             {
489                 const(char)[] cdr=pos1.sliceFrom()[1..$];
490                 if (prefs.foldDefaults && cdr.length>=car.length && cdr[0..car.length]==car){
491                     memmove(&output.data[pos.len],&output.data[pos1.len+1],output.length-pos1.len);
492                     output.length+=pos.len-pos1.len-1;
493                 }
494             } else {
495                 pos1.reset();
496             }
497 
498             return true;
499         }
500 
501         bool symbolName ( bool aliasHack = false)
502         out (result)
503         {
504             debug(traceDemangler) trace (result);
505         }
506         body
507         {
508             debug(traceDemangler) trace (aliasHack ? "symbolNameAH" : "symbolName");
509 
510     //      if (templateInstanceName (output))
511     //          return true;
512 
513             if (aliasHack){
514                 if (lNameAliasHack ())
515                     return true;
516             }
517             else
518             {
519                 if (lName ())
520                     return true;
521             }
522 
523             return false;
524         }
525 
526         bool lName ()
527         out (result)
528         {
529             debug(traceDemangler) trace (result);
530         }
531         body
532         {
533             debug(traceDemangler) trace ("lName");
534             auto pos=checkpoint();
535             uint chars;
536             if (! number (chars))
537                 return false;
538 
539             const(char)[] original = input;
540             version(all){
541                 if (input.length < chars) {
542                     // this may happen when the symbol gets hashed by MD5
543                     input = null;
544                     return true;        // try to continue
545                 }
546             }
547             
548             input = input[0 .. chars];
549             size_t len = input.length;
550             if (templateInstanceName())
551             {
552                 input = original[len - input.length .. $];
553                 return true;
554             }
555             input = original;
556 
557             if(!name (chars)){
558                 return pos.reset();
559             }
560             return true;
561         }
562 
563         /* this hack is ugly and guaranteed to break, but the symbols
564            generated for template alias parameters are broken:
565            the compiler generates a symbol of the form S(number){(number)(name)}
566            with no space between the numbers; what we do is try to match
567            different combinations of division between the concatenated numbers */
568 
569         bool lNameAliasHack ()
570         out (result)
571         {
572             debug(traceDemangler) trace (result);
573         }
574         body
575         {
576             debug(traceDemangler) trace ("lNameAH");
577 
578     //      uint chars;
579     //      if (! number (chars))
580     //          return false;
581 
582             uint chars;
583             auto pos=checkpoint();
584             if (! numberNoParse ())
585                 return false;
586             char[10] numberBuf;
587             const(char)[] str = pos.sliceFrom();
588             if (str.length>numberBuf.length){
589                 return pos.reset();
590             }
591             numberBuf[0..str.length]=str;
592             str=numberBuf[0..str.length];
593             pos.resetOutput();
594             
595             int i = 0;
596 
597             bool done = false;
598 
599             const(char)[] original = input;
600             const(char)[] working = input;
601 
602             while (done == false)
603             {
604                 if (i > 0)
605                 {
606                     input = working = original[0 .. toUint(str[0..i])];
607                 }
608                 else
609                     input = working = original;
610 
611                 chars = toUint(str[i..$]);
612 
613                 if (chars < input.length && chars > 0)
614                 {
615                     // cut the string from the right side to the number
616                     // const(char)[] original = input;
617                     // input = input[0 .. chars];
618                     // uint len = input.length;
619                     debug(traceDemangler) report ("trying {}/{}", chars, input.length);
620                     done = templateInstanceName ();
621                     //input = original[len - input.length .. $];
622 
623                     if (!done)
624                     {
625                         input = working;
626                         debug(traceDemangler) report ("trying {}/{}", chars, input.length);
627                         done = name (chars);
628                     }
629 
630                     if (done)
631                     {
632                         input = original[working.length - input.length .. $];
633                         return true;
634                     }
635                     else
636                         input = original;
637                 }
638 
639                 i += 1;
640                 if (i == str.length)
641                     return false;
642             }
643 
644             return true;
645         }
646 
647         bool number (ref uint value)
648         out (result)
649         {
650             debug(traceDemangler) trace (result);
651         }
652         body
653         {
654             debug(traceDemangler) trace ("number");
655 
656             if (input.length == 0)
657                 return false;
658 
659             value = 0;
660             if (input[0] >= '0' && input[0] <= '9')
661             {
662                 while (input.length > 0 && input[0] >= '0' && input[0] <= '9')
663                 {
664                     value = value * 10 + cast(uint) (input[0] - '0');
665                     consume (1);
666                 }
667                 return true;
668             }
669             else
670                 return false;
671         }
672 
673         bool numberNoParse ()
674         out (result)
675         {
676             debug(traceDemangler) trace (result);
677         }
678         body
679         {
680             debug(traceDemangler) trace ("numberNP");
681 
682             if (input.length == 0)
683                 return false;
684 
685             if (input[0] >= '0' && input[0] <= '9')
686             {
687                 while (input.length > 0 && input[0] >= '0' && input[0] <= '9')
688                 {
689                     output.append (input[0]);
690                     consume (1);
691                 }
692                 return true;
693             }
694             else
695                 return false;
696         }
697 
698         bool name (uint count)
699         out (result)
700         {
701             debug(traceDemangler) trace (result);
702         }
703         body
704         {
705             debug(traceDemangler) trace ("name");
706 
707             //if (input.length >= 3 && input[0 .. 3] == "__T")
708             //  return false; // workaround
709 
710             if (count > input.length)
711                 return false;
712 
713             const(char)[] name = consume (count);
714             output.append (name);
715             debug(traceDemangler) report (">>> name={}", name);
716 
717             return count > 0;
718         }
719 
720         bool type ()
721         out (result)
722         {
723             debug(traceDemangler) trace (result);
724         }
725         body
726         {
727             debug(traceDemangler) trace ("type");
728             if (! input.length) return false;
729             auto pos=checkpoint();
730             switch (input[0])
731             {
732                 case 'x':
733                     consume (1);
734                     output.append ("const ");
735                     if (!type ()) return pos.reset();
736                     return true;
737 
738                 case 'y':
739                     consume (1);
740                     output.append ("invariant ");
741                     if (!type ()) return pos.reset();
742                     return true;
743 
744                 case 'A':
745                     consume (1);
746                     if (type ())
747                     {
748                         output.append ("[]");
749                         return true;
750                     }
751                     return pos.reset();
752 
753                 case 'G':
754                     consume (1);
755                     uint size;
756                     if (! number (size))
757                         return false;
758                     if (type ()) {
759                         output.append ("[" ~ ctfe_i2a(size) ~ "]");
760                         return true;
761                     }
762                     return pos.reset();
763 
764                 case 'H':
765                     consume (1);
766                     auto pos2=checkpoint();
767                     if (! type ())
768                         return false;
769                     const(char)[] keytype=pos2.sliceFrom();
770                     output.append ("[");
771                     auto pos3=checkpoint();
772                     if (type ())
773                     {
774                         const(char)[] subtype=pos3.sliceFrom();
775                         output.append ("]");
776                         if (subtype.length<=keytype.length){
777                             auto pos4=checkpoint();
778                             output.append (keytype);
779                             memmove(&output.data[pos2.len],&output.data[pos3.len],subtype.length);
780                             output.data[pos2.len+keytype.length]='[';
781                             memcpy(&output.data[pos2.len],&output.data[pos4.len],keytype.length);
782                             pos4.reset();
783                         }
784                         return true;
785                     }
786                     return pos.reset();
787 
788                 case 'P':
789                     consume (1);
790                     if (type ())
791                     {
792                         output.append ("*");
793                         return true;
794                     }
795                     return false;
796                 case 'F': case 'U': case 'W': case 'V': case 'R': case 'D': case 'M':
797                     return typeFunction ();
798                 case 'I': case 'C': case 'S': case 'E': case 'T':
799                     return typeNamed ();
800                 case 'n':
801                     consume (1);
802                     output.append ("none");
803                     return true;
804                 case 'v':
805                     consume (1);
806                     output.append ("void");
807                     return true;
808                 case 'g':
809                     consume (1);
810                     output.append ("byte");
811                     return true;
812                 case 'h':
813                     consume (1);
814                     output.append ("ubyte");
815                     return true;
816                 case 's':
817                     consume (1);
818                     output.append ("short");
819                     return true;
820                 case 't':
821                     consume (1);
822                     output.append ("ushort");
823                     return true;
824                 case 'i':
825                     consume (1);
826                     output.append ("int");
827                     return true;
828                 case 'k':
829                     consume (1);
830                     output.append ("uint");
831                     return true;
832                 case 'l':
833                     consume (1);
834                     output.append ("long");
835                     return true;
836                 case 'm':
837                     consume (1);
838                     output.append ("ulong");
839                     return true;
840                 case 'f':
841                     consume (1);
842                     output.append ("float");
843                     return true;
844                 case 'd':
845                     consume (1);
846                     output.append ("double");
847                     return true;
848                 case 'e':
849                     consume (1);
850                     output.append ("real");
851                     return true;
852                 case 'q':
853                     consume(1);
854                     output.append ("cfloat");
855                     return true;
856                 case 'r':
857                     consume(1);
858                     output.append ("cdouble");
859                     return true;
860                 case 'c':
861                     consume(1);
862                     output.append ("creal");
863                     return true;
864                 case 'o':
865                     consume(1);
866                     output.append ("ifloat");
867                     return true;
868                 case 'p':
869                     consume(1);
870                     output.append ("idouble");
871                     return true;
872                 case 'j':
873                     consume(1);
874                     output.append ("ireal");
875                     return true;
876                 case 'b':
877                     consume (1);
878                     output.append ("bool");
879                     return true;
880                 case 'a':
881                     consume (1);
882                     output.append ("char");
883                     return true;
884                 case 'u':
885                     consume (1);
886                     output.append ("wchar");
887                     return true;
888                 case 'w':
889                     consume (1);
890                     output.append ("dchar");
891                     return true;
892                 case 'B':
893                     consume (1);
894                     uint count;
895                     if (! number (count))
896                         return pos.reset();
897                     output.append ('(');
898                     if (! arguments ())
899                         return pos.reset();
900                     output.append (')');
901                     return true;
902 
903                 default:
904                     return pos.reset();
905             }
906 
907             //return true;
908         }
909 
910         bool typeFunction ()
911         out (result)
912         {
913             debug(traceDemangler) trace (result);
914         }
915         body
916         {
917             debug(traceDemangler) trace ("typeFunction");
918             
919             auto pos=checkpoint();
920             bool isMethod = false;
921             bool isDelegate = false;
922 
923             if (input.length == 0)
924                 return false;
925 
926             if (input[0] == 'M')
927             {
928                 consume (1);
929                 isMethod = true;
930             }
931             if (input[0] == 'D')
932             {
933                 consume (1);
934                 isDelegate = true;
935                 assert (! isMethod);
936             }
937 
938             switch (input[0])
939             {
940                 case 'F':
941                     consume (1);
942                     break;
943 
944                 case 'U':
945                     consume (1);
946                     output.append ("extern(C) ");
947                     break;
948 
949                 case 'W':
950                     consume (1);
951                     output.append ("extern(Windows) ");
952                     break;
953 
954                 case 'V':
955                     consume (1);
956                     output.append ("extern(Pascal) ");
957                     break;
958 
959                 case 'R':
960                     consume (1);
961                     output.append ("extern(C++) ");
962                     break;
963 
964                 default:
965                     return pos.reset();
966             }
967             
968             auto pos2=checkpoint();
969             if (isMethod)
970                 output.append (" method (");
971             else if (isDelegate)
972                 output.append (" delegate (");
973             else
974                 output.append (" function (");
975             
976             arguments ();
977             version (all){
978                 if (0 == input.length) {
979                     // probably MD5 symbol hashing. try to continue
980                     return true;
981                 }
982             }
983             switch (input[0])
984             {
985                 case 'X': case 'Y': case 'Z':
986                     consume (1);
987                     break;
988                 default:
989                     return pos.reset();
990             }
991             output.append (")");
992             
993             auto pos3=checkpoint();
994             if (! type ())
995                 return pos.reset();
996             const(char)[] retT=pos3.sliceFrom();
997             auto pos4=checkpoint();
998             output.append(retT);
999             memmove(&output.data[pos2.len+retT.length],&output.data[pos2.len],pos3.len-pos2.len+1);
1000             memcpy(&output.data[pos2.len],&output.data[pos4.len],retT.length);
1001             pos4.reset();
1002             return true;
1003         }
1004 
1005         bool arguments ()
1006         out (result)
1007         {
1008             debug(traceDemangler) trace (result);
1009         }
1010         body
1011         {
1012             debug(traceDemangler) trace ("arguments");
1013 
1014             if (! argument ())
1015                 return false;
1016 
1017             auto pos=checkpoint();
1018             output.append (", ");
1019             if (!arguments ()){
1020                 pos.reset();
1021             }
1022 
1023             return true;
1024         }
1025 
1026         bool argument ()
1027         out (result)
1028         {
1029             debug(traceDemangler) trace (result);
1030         }
1031         body
1032         {
1033             debug(traceDemangler) trace ("argument");
1034 
1035             if (input.length == 0)
1036                 return false;
1037             auto pos=checkpoint();
1038             switch (input[0])
1039             {
1040                 case 'K':
1041                     consume (1);
1042                     output.append ("ref ");
1043                     break;
1044 
1045                 case 'J':
1046                     consume (1);
1047                     output.append ("out ");
1048                     break;
1049 
1050                 case 'L':
1051                     consume (1);
1052                     output.append ("lazy ");
1053                     break;
1054 
1055                 default:
1056             }
1057 
1058             if (! type ())
1059                 return pos.reset();
1060 
1061             return true;
1062         }
1063 
1064         bool typeNamed ()
1065         out (result)
1066         {
1067             debug(traceDemangler) trace (result);
1068         }
1069         body
1070         {
1071             debug(traceDemangler) trace ("typeNamed");
1072             auto pos=checkpoint();
1073             const(char)[] kind;
1074             switch (input[0])
1075             {
1076                 case 'I':
1077                     consume (1);
1078                     kind = "interface";
1079                     break;
1080 
1081                 case 'S':
1082                     consume (1);
1083                     kind = "struct";
1084                     break;
1085 
1086                 case 'C':
1087                     consume (1);
1088                     kind = "class";
1089                     break;
1090 
1091                 case 'E':
1092                     consume (1);
1093                     kind = "enum";
1094                     break;
1095 
1096                 case 'T':
1097                     consume (1);
1098                     kind = "typedef";
1099                     break;
1100 
1101                 default:
1102                     return false;
1103             }
1104 
1105             //output.append (kind);
1106             //output.append ("=");
1107 
1108             if (! qualifiedName ())
1109                 return pos.reset();
1110 
1111             if (prefs. printTypeKind)
1112             {
1113                 output.append ("<");
1114                 output.append (kind);
1115                 output.append (">");
1116             }
1117 
1118             return true;
1119         }
1120 
1121         bool templateInstanceName ()
1122         out (result)
1123         {
1124             debug(traceDemangler) trace (result);
1125         }
1126         body
1127         {
1128             debug(traceDemangler) trace ("templateInstanceName");
1129             auto pos=checkpoint();
1130             if (input.length < 4 || input[0..3] != "__T")
1131                 return false;
1132 
1133             consume (3);
1134 
1135             if (! lName ())
1136                 return checkpoint().reset();
1137 
1138             output.append ("!(");
1139 
1140             _templateDepth++;
1141             if (_templateDepth <= prefs.templateExpansionDepth) {
1142                 templateArgs ();
1143             } else {
1144                 auto pos2=checkpoint();
1145                 templateArgs ();
1146                 pos2.resetOutput();
1147                 output.append ("...");
1148             }
1149             _templateDepth--;
1150 
1151             if (input.length > 0 && input[0] != 'Z')
1152                 return pos.reset();
1153 
1154             output.append (")");
1155 
1156             consume (1);
1157             return true;
1158         }
1159 
1160         bool templateArgs ()
1161         out (result)
1162         {
1163             debug(traceDemangler) trace (result);
1164         }
1165         body
1166         {
1167             debug(traceDemangler) trace ("templateArgs");
1168 
1169             if (! templateArg ())
1170                 return false;
1171             auto pos1=checkpoint();
1172             output.append (", ");
1173             if (! templateArgs ())
1174             {
1175                 pos1.reset();
1176             }
1177 
1178             return true;
1179         }
1180 
1181         bool templateArg ()
1182         out (result)
1183         {
1184             debug(traceDemangler) trace (result);
1185         }
1186         body
1187         {
1188             debug(traceDemangler) trace ("templateArg");
1189 
1190             if (input.length == 0)
1191                 return false;
1192             auto pos=checkpoint();
1193             switch (input[0])
1194             {
1195                 case 'T':
1196                     consume (1);
1197                     if (! type ())
1198                         return pos.reset();
1199                     return true;
1200 
1201                 case 'V':
1202                     consume (1);
1203                     auto pos2=checkpoint();
1204                     if (! type ())
1205                         return pos.reset();
1206                     pos2.resetOutput();
1207                     if (! value ())
1208                         return pos.reset();
1209                     return true;
1210 
1211                 case 'S':
1212                     consume (1);
1213                     if (! qualifiedName (true))
1214                         return pos.reset();
1215                     return true;
1216 
1217                 default:
1218                     return pos.reset();
1219             }
1220 
1221             //return pos.reset;
1222         }
1223 
1224         bool value ()
1225         out (result)
1226         {
1227             debug(traceDemangler) trace (result);
1228         }
1229         body
1230         {
1231             debug(traceDemangler) trace ("value");
1232 
1233             if (input.length == 0)
1234                 return false;
1235 
1236             auto pos=checkpoint();
1237 
1238             switch (input[0])
1239             {
1240                 case 'n':
1241                     consume (1);
1242                     return true;
1243 
1244                 case 'N':
1245                     consume (1);
1246                     output.append ('-');
1247                     if (! numberNoParse ())
1248                         return pos.reset();
1249                     return true;
1250 
1251                 case 'e':
1252                     consume (1);
1253                     if (! hexFloat ())
1254                         return pos.reset();
1255                     return true;
1256 
1257                 case 'c': //TODO
1258 
1259                 case 'A':
1260                     consume (1);
1261                     uint count;
1262                     if (! number (count))
1263                         return pos.reset();
1264                     if (count>0) {
1265                         output.append ("[");
1266                         for (uint i = 0; i < count-1; i++)
1267                         {
1268                             if (! value ())
1269                                 return pos.reset();
1270                             output.append (", ");
1271                         }
1272                         if (! value ())
1273                             return pos.reset();
1274                     }
1275                     output.append ("]");
1276                     return true;
1277 
1278                 default:
1279                     if (! numberNoParse ())
1280                         return pos.reset();
1281                     return true;
1282             }
1283 
1284             //return pos.reset();
1285         }
1286 
1287         bool hexFloat ()
1288         out (result)
1289         {
1290             debug(traceDemangler) trace (result);
1291         }
1292         body
1293         {
1294             debug(traceDemangler) trace ("hexFloat");
1295 
1296             auto pos=checkpoint();
1297             if (input[0 .. 3] == "NAN")
1298             {
1299                 consume (3);
1300                 output.append ("nan");
1301                 return true;
1302             }
1303             else if (input[0 .. 3] == "INF")
1304             {
1305                 consume (3);
1306                 output.append ("+inf");
1307                 return true;
1308             }
1309             else if (input[0 .. 3] == "NINF")
1310             {
1311                 consume (3);
1312                 output.append ("-inf");
1313                 return true;
1314             }
1315 
1316             bool negative = false;
1317             if (input[0] == 'N')
1318             {
1319                 consume (1);
1320                 negative = true;
1321             }
1322 
1323             ulong num;
1324             if (! hexNumber (num))
1325                 return false;
1326 
1327             if (input[0] != 'P')
1328                 return false;
1329             consume (1);
1330 
1331             bool negative_exponent = false;
1332             if (input[0] == 'N')
1333             {
1334                 consume (1);
1335                 negative_exponent = true;
1336             }
1337 
1338             uint exponent;
1339             if (! number (exponent))
1340                 return pos.reset();
1341 
1342             return true;
1343         }
1344 
1345         static bool isHexDigit (char c)
1346         {
1347             return (c > '0' && c <'9') || (c > 'a' && c < 'f') || (c > 'A' && c < 'F');
1348         }
1349 
1350         bool hexNumber (ref ulong value)
1351         out (result)
1352         {
1353             debug(traceDemangler) trace (result);
1354         }
1355         body
1356         {
1357             debug(traceDemangler) trace ("hexFloat");
1358 
1359             if (isHexDigit (input[0]))
1360             {
1361                 while (isHexDigit (input[0]))
1362                 {
1363                     //output.append (input[0]);
1364                     consume (1);
1365                 }
1366                 return true;
1367             }
1368             else
1369                 return false;
1370         }
1371     }
1372 }
1373 
1374 
1375 private struct Buffer
1376 {
1377     char[] data;
1378     size_t     length;
1379 
1380     void append (const(char)[] s)
1381     {
1382         assert(this.length+s.length<=data.length);
1383         size_t len=this.length+s.length;
1384         if (len>data.length) len=data.length;
1385         data[this.length .. len] = s[0..len-this.length];
1386         this.length = len;
1387     }
1388 
1389     void append (char c)
1390     {
1391         assert(this.length<data.length);
1392         data[this.length .. this.length + 1] = c;
1393         this.length += 1;
1394     }
1395 
1396     void append (Buffer b)
1397     {
1398         append (b.slice());
1399     }
1400 
1401     const(char)[] slice() ()
1402     {
1403         return data[0 .. this.length];
1404     }
1405 }
1406 
1407 /// The default demangler.
1408 static Demangler demangler;
1409 
1410 static this(){
1411     demangler=new Demangler(1);
1412 }
1413 
1414 }
1415 else
1416 {
1417 import core = core.demangle;
1418 
1419 public class Demangler
1420 {
1421     /** Demangles the given string. */
1422     public inout(char)[] demangle (inout(char)[] input)
1423     {
1424         return cast(typeof(return))core.demangle(input);
1425     }
1426 
1427     /** Demangles the given string using output to hold the result. */
1428     public inout(char)[] demangle (inout(char)[] input, char[] output)
1429     {
1430         return cast(typeof(return))core.demangle(input, output);
1431     }
1432 }
1433 
1434 /// The default demangler.
1435 static Demangler demangler;
1436 
1437 static this(){
1438     demangler=new Demangler;
1439 }
1440 
1441 }