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