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;
}
}
|