| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442 | /** * D symbol name demangling * * Attempts to demangle D symbols generated by the DMD frontend. * (Which is not always technically possible.) * * A sample program demangling the names passed as arguments. * --- * module demangle; * import tango.core.tools.Demangler; * import tango.io.Stdout; * * void usage(){ * Stdout("demangle [--help] [--level 0-9] mangledName1 [mangledName2...]").newline; * } * * int main(const(char)[][]args){ * uint start=1; * if (args.length>1) { * if (args[start]=="--help"){ * usage(); * ++start; * } * if (args[start]=="--level"){ * ++start; * if (args.length==start || args[start].length!=1 || args[start][0]<'0' || * args[start][0]>'9') { * Stdout("invalid level '")((args.length==start)?"*missing*":args[start]) * ("' (must be 0-9)").newline; * usage(); * return 2; * } * demangler.verbosity=args[start+1][0]-'0'; * ++start; * } * } else { * usage(); * return 0; * } * foreach (n;args[start..$]){ * Stdout(demangler.demangle(n)).newline; * } * return 0; * } * --- * Copyright: Copyright (C) 2007-2008 Zygfryd (aka Hxal), Fawzi. All rights reserved. * License: tango license, apache 2.0 * Authors: Zygfryd (aka Hxal), Fawzi * */ module tango.core.tools.Demangler; version(TangoDemangler) { import tango.core.Traits: ctfe_i2a; import tango.stdc.string: memmove,memcpy; debug(traceDemangler) import tango.io.Stdout; version(DigitalMars) version(Windows) { bool isMD5Hashed(const(char)[] name) { if (name.length < 34 || (name.length >= 2 && name[0..2] != "_D")) { return false; } foreach (c; name[$-32..$]) { if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) { return false; } } return true; } const(char)[] decompressOMFSymbol(const(char)[] garbled, char[]* buf) { int ungarbledLength = 0; bool compressed = false; for (int ci = 0; ci < garbled.length; ++ci) { char c = garbled[ci]; if (0 == (c & 0x80)) { ++ungarbledLength; } else { compressed = true; int matchLen = void; if (c & 0x40) { matchLen = (c & 0b111) + 1; } else { if (ci+2 >= garbled.length) { return garbled; } else { matchLen = cast(int)(c & 0x38) << 4; matchLen += garbled[ci+1] & ~0x80; ci += 2; } } ungarbledLength += matchLen; } } if (!compressed || ungarbledLength > (*buf).length) { return garbled; } else { char[] ungarbled = (*buf)[$-ungarbledLength..$]; *buf = (*buf)[0..$-ungarbledLength]; int ui = 0; for (int ci = 0; ci < garbled.length; ++ci) { char c = garbled[ci]; if (0 == (c & 0x80)) { ungarbled[ui++] = c; } else { int matchOff = void; int matchLen = void; if (c & 0x40) { matchOff = ((c >> 3) & 0b111) + 1; matchLen = (c & 0b111) + 1; } else { matchOff = cast(int)(c & 0b111) << 7; matchLen = cast(int)(c & 0x38) << 4; matchLen += garbled[ci+1] & ~0x80; matchOff += garbled[ci+2] & ~0x80; ci += 2; } int matchStart = ui - matchOff; if (matchStart + matchLen > ui) { // fail return garbled; } const(char)[] match = ungarbled[matchStart .. matchStart + matchLen]; ungarbled[ui .. ui+matchLen] = match; ui += matchLen; } } return ungarbled; } } } /// decompresses a symbol and returns the full symbol, and possibly a reduced buffer space /// (does something only on windows with DMD.) const(char)[] decompressSymbol(const(char)[] func,char[] *buf){ version(DigitalMars) version(Windows){ if (isMD5Hashed(func)) { func = func[0..$-32]; } func = decompressOMFSymbol(func, buf); } return func; } uint toUint(const(char)[] s){ uint res=0; for (int i=0;i<s.length;++i){ if (s[i]>='0'&& s[i]<='9'){ res*=10; res+=s[i]-'0'; } else { assert(false); } } return res; } /** * Flexible demangler. * Attempts to demangle D symbols generated by the DMD frontend. * (Which is not always technically possible.) */ public class Demangler { /** How deeply to recurse printing template parameters, * for depths greater than this, an ellipsis is used. */ uint templateExpansionDepth = 1; /** Skip default members of templates (sole members named after * the template.) */ bool foldDefaults = false; /** Print types of functions being part of the main symbol. */ bool expandFunctionTypes = false; /** For composite types, print the kind (class|struct|etc.) of the type. */ bool printTypeKind = false; /** Sets the verbosity level of the demangler (template expansion level,...) */ public void verbosity (uint level) { switch (level) { case 0: templateExpansionDepth = 0; expandFunctionTypes = false; printTypeKind = false; break; case 1: templateExpansionDepth = 1; expandFunctionTypes = false; printTypeKind = false; break; case 2: templateExpansionDepth = 1; expandFunctionTypes = false; printTypeKind = true; break; case 3: templateExpansionDepth = 1; expandFunctionTypes = true; printTypeKind = true; break; default: templateExpansionDepth = level - 2; expandFunctionTypes = true; printTypeKind = true; } } /** Creates a demangler. */ this () { verbosity (1); } /** Creates a demangler with the given verbosity level. */ this (uint verbosityLevel) { verbosity (verbosityLevel); } /** Demangles the given string. */ public inout(char)[] demangle (inout(char)[] input) { // Special case for "_Dmain" if (input == "_Dmain") return "main"; char[4096] buf=void; auto res=DemangleInstance(this,input,buf); if (res.mangledName() && res.input.length==0){ return cast(inout(char)[])res.slice().dup; } else { if (res.slice().length) res.output.append(" "); if (res.type() && res.input.length==0){ return cast(inout(char)[])res.slice().dup; } else { return input; } } } /** Demangles the given string using output to hold the result. */ public inout(char)[] demangle (inout(char)[] input, char[] output) { // Special case for "_Dmain" if (input == "_Dmain") return "main"; auto res=DemangleInstance(this,input,output); if (res.mangledName () && res.input.length==0) { return cast(inout(char)[])res.slice(); } else { if (res.slice().length) res.output.append(" "); if (res.type() && res.input.length==0) { return cast(inout(char)[])res.slice(); } else { return input; } } } /// This represents a single demangling request, and is the place where the real work is done /// some more cleanup would probably be in order (maybe remove Buffer.) struct DemangleInstance{ debug(traceDemangler) private const(char)[][] _trace; private const(char)[] input; private uint _templateDepth; Buffer output; Demangler prefs; struct BufState{ DemangleInstance* dem; const(char)[] input; size_t len; static BufState opCall(ref DemangleInstance dem){ BufState res; res.dem=&dem; res.len=dem.output.length; res.input=dem.input; return res; } // resets input and output buffers and returns false bool reset(){ dem.output.length=len; dem.input=input; return false; } // resets only the output buffer and returns false bool resetOutput(){ dem.output.length=len; return false; } const(char)[] sliceFrom(){ return dem.output.data[len..dem.output.length]; } } BufState checkpoint(){ return BufState(this); } static DemangleInstance opCall(Demangler prefs,const(char)[] input,const(char)[] output){ char[] buff = new char[output.length]; input = decompressSymbol(input, &buff); output = buff; DemangleInstance res; res.prefs=prefs; res.input=input; res._templateDepth=0; res.output.data=buff; debug(traceDemangler) res._trace=null; return res; } debug (traceDemangler) { private void trace (const(char)[] where) { if (_trace.length > 500) throw new Exception ("Infinite recursion"); int len=_trace.length; const(char)[] spaces = " "; spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)]; if (input.length < 50) Stdout.formatln ("{}{} : {{{}}", spaces, where, input); else Stdout.formatln ("{}{} : {{{}}", spaces, where, input[0..50]); _trace ~= where; } private void report (T...) (const(char)[] fmt, T args) { int len=_trace.length; const(char)[] spaces = " "; spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)]; Stdout (spaces); Stdout.formatln (fmt, args); } private void trace (bool result) { //auto tmp = _trace[$-1]; _trace = _trace[0..$-1]; int len=_trace.length; const(char)[] spaces = " "; spaces=spaces[0 .. ((len<spaces.length)?len:spaces.length)]; Stdout(spaces); if (!result) Stdout.formatln ("fail"); else Stdout.formatln ("success"); } } const(char)[] slice(){ return output.slice(); } private const(char)[] consume (uint amt) { const(char)[] tmp = input[0 .. amt]; input = input[amt .. $]; return tmp; } bool mangledName () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("mangledName"); if (input.length<2) return false; if (input[0]=='D'){ consume(1); } else if (input[0..2] == "_D") { consume(2); } else { return false; } if (! typedqualifiedName ()) return false; if (input.length > 0) { auto pos1=checkpoint(); output.append("<"); if (! type ()) pos1.reset(); // return false?? else if (prefs.printTypeKind){ output.append(">"); } else { pos1.resetOutput(); } } //Stdout.formatln ("mangledName={}", namebuf.slice); return true; } bool typedqualifiedName () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("typedqualifiedName"); auto posCar=checkpoint(); if (! symbolName ()) return false; const(char)[] car=posCar.sliceFrom(); // undocumented auto pos=checkpoint(); output.append ("{"); if (typeFunction ()){ if (!prefs. expandFunctionTypes){ pos.resetOutput(); } else { output.append ("}"); } } else { pos.reset(); } pos=checkpoint(); output.append ("."); if (typedqualifiedName ()) { if (prefs.foldDefaults && car.length<pos.sliceFrom().length && car==pos.sliceFrom()[1..car.length+1]){ memmove(&output.data[posCar.len],&output.data[pos.len+1],output.length-pos.len); output.length+=posCar.len-pos.len-1; } } else { pos.reset(); } return true; } bool qualifiedName (bool aliasHack = false) out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace (aliasHack ? "qualifiedNameAH" : "qualifiedName"); auto pos=checkpoint(); if (! symbolName (aliasHack)) return false; const(char)[] car=pos.sliceFrom(); auto pos1=checkpoint(); output.append ("."); if (typedqualifiedName ()) { const(char)[] cdr=pos1.sliceFrom()[1..$]; if (prefs.foldDefaults && cdr.length>=car.length && cdr[0..car.length]==car){ memmove(&output.data[pos.len],&output.data[pos1.len+1],output.length-pos1.len); output.length+=pos.len-pos1.len-1; } } else { pos1.reset(); } return true; } bool symbolName ( bool aliasHack = false) out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace (aliasHack ? "symbolNameAH" : "symbolName"); // if (templateInstanceName (output)) // return true; if (aliasHack){ if (lNameAliasHack ()) return true; } else { if (lName ()) return true; } return false; } bool lName () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("lName"); auto pos=checkpoint(); uint chars; if (! number (chars)) return false; const(char)[] original = input; version(all){ if (input.length < chars) { // this may happen when the symbol gets hashed by MD5 input = null; return true; // try to continue } } input = input[0 .. chars]; size_t len = input.length; if (templateInstanceName()) { input = original[len - input.length .. $]; return true; } input = original; if(!name (chars)){ return pos.reset(); } return true; } /* this hack is ugly and guaranteed to break, but the symbols generated for template alias parameters are broken: the compiler generates a symbol of the form S(number){(number)(name)} with no space between the numbers; what we do is try to match different combinations of division between the concatenated numbers */ bool lNameAliasHack () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("lNameAH"); // uint chars; // if (! number (chars)) // return false; uint chars; auto pos=checkpoint(); if (! numberNoParse ()) return false; char[10] numberBuf; const(char)[] str = pos.sliceFrom(); if (str.length>numberBuf.length){ return pos.reset(); } numberBuf[0..str.length]=str; str=numberBuf[0..str.length]; pos.resetOutput(); int i = 0; bool done = false; const(char)[] original = input; const(char)[] working = input; while (done == false) { if (i > 0) { input = working = original[0 .. toUint(str[0..i])]; } else input = working = original; chars = toUint(str[i..$]); if (chars < input.length && chars > 0) { // cut the string from the right side to the number // const(char)[] original = input; // input = input[0 .. chars]; // uint len = input.length; debug(traceDemangler) report ("trying {}/{}", chars, input.length); done = templateInstanceName (); //input = original[len - input.length .. $]; if (!done) { input = working; debug(traceDemangler) report ("trying {}/{}", chars, input.length); done = name (chars); } if (done) { input = original[working.length - input.length .. $]; return true; } else input = original; } i += 1; if (i == str.length) return false; } return true; } bool number (ref uint value) out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("number"); if (input.length == 0) return false; value = 0; if (input[0] >= '0' && input[0] <= '9') { while (input.length > 0 && input[0] >= '0' && input[0] <= '9') { value = value * 10 + cast(uint) (input[0] - '0'); consume (1); } return true; } else return false; } bool numberNoParse () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("numberNP"); if (input.length == 0) return false; if (input[0] >= '0' && input[0] <= '9') { while (input.length > 0 && input[0] >= '0' && input[0] <= '9') { output.append (input[0]); consume (1); } return true; } else return false; } bool name (uint count) out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("name"); //if (input.length >= 3 && input[0 .. 3] == "__T") // return false; // workaround if (count > input.length) return false; const(char)[] name = consume (count); output.append (name); debug(traceDemangler) report (">>> name={}", name); return count > 0; } bool type () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("type"); if (! input.length) return false; auto pos=checkpoint(); switch (input[0]) { case 'x': consume (1); output.append ("const "); if (!type ()) return pos.reset(); return true; case 'y': consume (1); output.append ("invariant "); if (!type ()) return pos.reset(); return true; case 'A': consume (1); if (type ()) { output.append ("[]"); return true; } return pos.reset(); case 'G': consume (1); uint size; if (! number (size)) return false; if (type ()) { output.append ("[" ~ ctfe_i2a(size) ~ "]"); return true; } return pos.reset(); case 'H': consume (1); auto pos2=checkpoint(); if (! type ()) return false; const(char)[] keytype=pos2.sliceFrom(); output.append ("["); auto pos3=checkpoint(); if (type ()) { const(char)[] subtype=pos3.sliceFrom(); output.append ("]"); if (subtype.length<=keytype.length){ auto pos4=checkpoint(); output.append (keytype); memmove(&output.data[pos2.len],&output.data[pos3.len],subtype.length); output.data[pos2.len+keytype.length]='['; memcpy(&output.data[pos2.len],&output.data[pos4.len],keytype.length); pos4.reset(); } return true; } return pos.reset(); case 'P': consume (1); if (type ()) { output.append ("*"); return true; } return false; case 'F': case 'U': case 'W': case 'V': case 'R': case 'D': case 'M': return typeFunction (); case 'I': case 'C': case 'S': case 'E': case 'T': return typeNamed (); case 'n': consume (1); output.append ("none"); return true; case 'v': consume (1); output.append ("void"); return true; case 'g': consume (1); output.append ("byte"); return true; case 'h': consume (1); output.append ("ubyte"); return true; case 's': consume (1); output.append ("short"); return true; case 't': consume (1); output.append ("ushort"); return true; case 'i': consume (1); output.append ("int"); return true; case 'k': consume (1); output.append ("uint"); return true; case 'l': consume (1); output.append ("long"); return true; case 'm': consume (1); output.append ("ulong"); return true; case 'f': consume (1); output.append ("float"); return true; case 'd': consume (1); output.append ("double"); return true; case 'e': consume (1); output.append ("real"); return true; case 'q': consume(1); output.append ("cfloat"); return true; case 'r': consume(1); output.append ("cdouble"); return true; case 'c': consume(1); output.append ("creal"); return true; case 'o': consume(1); output.append ("ifloat"); return true; case 'p': consume(1); output.append ("idouble"); return true; case 'j': consume(1); output.append ("ireal"); return true; case 'b': consume (1); output.append ("bool"); return true; case 'a': consume (1); output.append ("char"); return true; case 'u': consume (1); output.append ("wchar"); return true; case 'w': consume (1); output.append ("dchar"); return true; case 'B': consume (1); uint count; if (! number (count)) return pos.reset(); output.append ('('); if (! arguments ()) return pos.reset(); output.append (')'); return true; default: return pos.reset(); } //return true; } bool typeFunction () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("typeFunction"); auto pos=checkpoint(); bool isMethod = false; bool isDelegate = false; if (input.length == 0) return false; if (input[0] == 'M') { consume (1); isMethod = true; } if (input[0] == 'D') { consume (1); isDelegate = true; assert (! isMethod); } switch (input[0]) { case 'F': consume (1); break; case 'U': consume (1); output.append ("extern(C) "); break; case 'W': consume (1); output.append ("extern(Windows) "); break; case 'V': consume (1); output.append ("extern(Pascal) "); break; case 'R': consume (1); output.append ("extern(C++) "); break; default: return pos.reset(); } auto pos2=checkpoint(); if (isMethod) output.append (" method ("); else if (isDelegate) output.append (" delegate ("); else output.append (" function ("); arguments (); version (all){ if (0 == input.length) { // probably MD5 symbol hashing. try to continue return true; } } switch (input[0]) { case 'X': case 'Y': case 'Z': consume (1); break; default: return pos.reset(); } output.append (")"); auto pos3=checkpoint(); if (! type ()) return pos.reset(); const(char)[] retT=pos3.sliceFrom(); auto pos4=checkpoint(); output.append(retT); memmove(&output.data[pos2.len+retT.length],&output.data[pos2.len],pos3.len-pos2.len+1); memcpy(&output.data[pos2.len],&output.data[pos4.len],retT.length); pos4.reset(); return true; } bool arguments () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("arguments"); if (! argument ()) return false; auto pos=checkpoint(); output.append (", "); if (!arguments ()){ pos.reset(); } return true; } bool argument () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("argument"); if (input.length == 0) return false; auto pos=checkpoint(); switch (input[0]) { case 'K': consume (1); output.append ("ref "); break; case 'J': consume (1); output.append ("out "); break; case 'L': consume (1); output.append ("lazy "); break; default: } if (! type ()) return pos.reset(); return true; } bool typeNamed () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("typeNamed"); auto pos=checkpoint(); const(char)[] kind; switch (input[0]) { case 'I': consume (1); kind = "interface"; break; case 'S': consume (1); kind = "struct"; break; case 'C': consume (1); kind = "class"; break; case 'E': consume (1); kind = "enum"; break; case 'T': consume (1); kind = "typedef"; break; default: return false; } //output.append (kind); //output.append ("="); if (! qualifiedName ()) return pos.reset(); if (prefs. printTypeKind) { output.append ("<"); output.append (kind); output.append (">"); } return true; } bool templateInstanceName () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("templateInstanceName"); auto pos=checkpoint(); if (input.length < 4 || input[0..3] != "__T") return false; consume (3); if (! lName ()) return checkpoint().reset(); output.append ("!("); _templateDepth++; if (_templateDepth <= prefs.templateExpansionDepth) { templateArgs (); } else { auto pos2=checkpoint(); templateArgs (); pos2.resetOutput(); output.append ("..."); } _templateDepth--; if (input.length > 0 && input[0] != 'Z') return pos.reset(); output.append (")"); consume (1); return true; } bool templateArgs () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("templateArgs"); if (! templateArg ()) return false; auto pos1=checkpoint(); output.append (", "); if (! templateArgs ()) { pos1.reset(); } return true; } bool templateArg () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("templateArg"); if (input.length == 0) return false; auto pos=checkpoint(); switch (input[0]) { case 'T': consume (1); if (! type ()) return pos.reset(); return true; case 'V': consume (1); auto pos2=checkpoint(); if (! type ()) return pos.reset(); pos2.resetOutput(); if (! value ()) return pos.reset(); return true; case 'S': consume (1); if (! qualifiedName (true)) return pos.reset(); return true; default: return pos.reset(); } //return pos.reset; } bool value () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("value"); if (input.length == 0) return false; auto pos=checkpoint(); switch (input[0]) { case 'n': consume (1); return true; case 'N': consume (1); output.append ('-'); if (! numberNoParse ()) return pos.reset(); return true; case 'e': consume (1); if (! hexFloat ()) return pos.reset(); return true; case 'c': //TODO case 'A': consume (1); uint count; if (! number (count)) return pos.reset(); if (count>0) { output.append ("["); for (uint i = 0; i < count-1; i++) { if (! value ()) return pos.reset(); output.append (", "); } if (! value ()) return pos.reset(); } output.append ("]"); return true; default: if (! numberNoParse ()) return pos.reset(); return true; } //return pos.reset(); } bool hexFloat () out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("hexFloat"); auto pos=checkpoint(); if (input[0 .. 3] == "NAN") { consume (3); output.append ("nan"); return true; } else if (input[0 .. 3] == "INF") { consume (3); output.append ("+inf"); return true; } else if (input[0 .. 3] == "NINF") { consume (3); output.append ("-inf"); return true; } bool negative = false; if (input[0] == 'N') { consume (1); negative = true; } ulong num; if (! hexNumber (num)) return false; if (input[0] != 'P') return false; consume (1); bool negative_exponent = false; if (input[0] == 'N') { consume (1); negative_exponent = true; } uint exponent; if (! number (exponent)) return pos.reset(); return true; } static bool isHexDigit (char c) { return (c > '0' && c <'9') || (c > 'a' && c < 'f') || (c > 'A' && c < 'F'); } bool hexNumber (ref ulong value) out (result) { debug(traceDemangler) trace (result); } body { debug(traceDemangler) trace ("hexFloat"); if (isHexDigit (input[0])) { while (isHexDigit (input[0])) { //output.append (input[0]); consume (1); } return true; } else return false; } } } private struct Buffer { char[] data; size_t length; void append (const(char)[] s) { assert(this.length+s.length<=data.length); size_t len=this.length+s.length; if (len>data.length) len=data.length; data[this.length .. len] = s[0..len-this.length]; this.length = len; } void append (char c) { assert(this.length<data.length); data[this.length .. this.length + 1] = c; this.length += 1; } void append (Buffer b) { append (b.slice()); } const(char)[] slice() () { return data[0 .. this.length]; } } /// The default demangler. static Demangler demangler; static this(){ demangler=new Demangler(1); } } else { import core = core.demangle; public class Demangler { /** Demangles the given string. */ public inout(char)[] demangle (inout(char)[] input) { return cast(typeof(return))core.demangle(input); } /** Demangles the given string using output to hold the result. */ public inout(char)[] demangle (inout(char)[] input, char[] output) { return cast(typeof(return))core.demangle(input, output); } } /// The default demangler. static Demangler demangler; static this(){ demangler=new Demangler; } } |