1 /** 2 Parses DMD JSON output and builds up a documentation syntax tree (JSON format from DMD 2.063.2). 3 4 Copyright: © 2012-2016 RejectedSoftware e.K. 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module ddox.parsers.jsonparser; 9 10 import ddox.ddox; 11 import ddox.entities; 12 13 import std.algorithm; 14 import std.conv; 15 import std.exception; 16 import std.range; 17 import std.stdio; 18 import std.string; 19 import std.typecons; 20 import core.demangle; 21 import vibe.core.log; 22 import vibe.data.json; 23 24 25 Package parseJsonDocs(Json json, Package root = null) 26 { 27 if( !root ) root = new Package(null, null); 28 Parser p; 29 foreach (mod; json) 30 p.parseModuleDecls(mod, root); 31 p.parseTypes(); 32 return root; 33 } 34 35 private struct Parser 36 { 37 // global map of type declarations with all partially qualified names 38 // used to lookup type names for which the regular lookup has failed 39 private Declaration[string] m_typeMap; 40 41 Tuple!(Declaration, Json)[] m_declarations; 42 43 void parseModuleDecls(Json json, Package root_package) 44 { 45 Module mod; 46 if( "name" !in json ){ 47 logError("No name attribute in module %s - ignoring", json["filename"].opt!string); 48 return; 49 } 50 auto path = json["name"].get!string.split("."); 51 Package p = root_package; 52 foreach (i, pe; path) { 53 if( i+1 < path.length ) p = p.getOrAddPackage(pe); 54 else mod = p.createModule(pe); 55 } 56 57 mod.file = json["file"].get!string; 58 mod.docGroup = new DocGroup(mod, json["comment"].opt!string()); 59 mod.members = parseDeclList(json["members"], mod); 60 } 61 62 void parseTypes() 63 { 64 foreach (d; m_declarations) { 65 auto decl = d[0]; 66 auto json = d[1]; 67 final switch (decl.kind) { 68 case DeclarationKind.Variable: { 69 auto v = cast(VariableDeclaration)decl; 70 v.type = parseType(json, v); 71 } break; 72 case DeclarationKind.Function: { 73 auto f = cast(FunctionDeclaration)decl; 74 f.type = parseType(json, f, "void()"); 75 if (f.type.kind != TypeKind.Function) { 76 logError("Function %s has non-function type: %s", f.qualifiedName, f.type.kind); 77 break; 78 } 79 f.returnType = f.type.returnType; 80 f.attributes ~= f.type.attributes ~ f.type.modifiers; 81 82 auto params = json["parameters"].opt!(Json[]); 83 if (!params) { 84 params.length = f.type.parameterTypes.length; 85 foreach (i, pt; f.type.parameterTypes) { 86 auto jp = Json.emptyObject; 87 jp["name"] = f.type._parameterNames[i]; 88 jp["type"] = pt.text; 89 if (f.type._parameterDefaultValues[i]) 90 jp["default"] = f.type._parameterDefaultValues[i].valueString; 91 params[i] = jp; 92 } 93 } 94 95 f.parameters.reserve(params.length); 96 foreach (i, p; params) { 97 auto pname = p["name"].opt!string; 98 auto pdecl = new VariableDeclaration(f, pname); 99 pdecl.type = parseType(p, f); 100 foreach (sc; p["storageClass"].opt!(Json[])) 101 if (!pdecl.attributes.canFind(sc.get!string)) 102 pdecl.attributes ~= CachedString(sc.get!string); 103 if (auto pdv = "default" in p) 104 pdecl.initializer = parseValue(pdv.opt!string); 105 f.parameters ~= pdecl; 106 } 107 } break; 108 case DeclarationKind.Struct: break; 109 case DeclarationKind.Union: break; 110 case DeclarationKind.Class: { 111 auto c = cast(ClassDeclaration)decl; 112 if (!c.qualifiedName.equal("object.Object")) { 113 c.baseClass = parseType(json["base"], c, "Object", false); 114 } 115 foreach (intf; json["interfaces"].opt!(Json[])) 116 c.derivedInterfaces ~= CachedType(parseType(intf, c)); 117 } break; 118 case DeclarationKind.Interface: { 119 auto i = cast(InterfaceDeclaration)decl; 120 foreach (intf; json["interfaces"].opt!(Json[])) 121 i.derivedInterfaces ~= CachedType(parseType(intf, i)); 122 } break; 123 case DeclarationKind.Enum: { 124 auto e = cast(EnumDeclaration)decl; 125 e.baseType = parseType(json["base"], e); 126 } break; 127 case DeclarationKind.EnumMember: break; 128 case DeclarationKind.Alias: { 129 auto a = cast(AliasDeclaration)decl; 130 a.targetType = parseType(json, a, null); 131 } break; 132 case DeclarationKind.Template: break; 133 case DeclarationKind.TemplateParameter: 134 auto tp = cast(TemplateParameterDeclaration)decl; 135 if (json["kind"] == "value") 136 tp.type = parseType(json, tp, null); 137 break; 138 } 139 } 140 } 141 142 Declaration[] parseDeclList(Json json, Entity parent) 143 { 144 if( json.type == Json.Type.Undefined ) return null; 145 DocGroup lastdoc; 146 Declaration[] ret; 147 foreach( mem; json ){ 148 auto decl = parseDecl(mem, parent); 149 if( !decl ) continue; 150 auto doc = decl.docGroup; 151 if( lastdoc && (doc.text == lastdoc.text && doc.text.length || doc.comment.isDitto) ){ 152 lastdoc.members ~= decl; 153 decl.docGroup = lastdoc; 154 } else if( doc.comment.isPrivate ){ 155 decl.protection = Protection.Private; 156 lastdoc = null; 157 } else lastdoc = decl.docGroup; 158 ret ~= decl; 159 } 160 return ret; 161 } 162 163 Declaration parseDecl(Json json, Entity parent) 164 { 165 Declaration ret; 166 167 // DMD outputs templates with the wrong kind sometimes 168 if (json["name"].get!string().canFind('(') && json["kind"] != "mixin") { 169 ret = parseTemplateDecl(json, parent); 170 } else { 171 switch( json["kind"].get!string ){ 172 default: 173 logWarn("Unknown declaration kind: %s", json["kind"].get!string); 174 return null; 175 case "generated function": // generated functions are never documented 176 return null; 177 case "import": 178 case "static import": 179 // TODO: use for symbol resolving 180 return null; 181 case "destructor": return null; 182 case "mixin": return null; 183 case "alias": 184 ret = parseAliasDecl(json, parent); 185 break; 186 case "function": 187 case "allocator": 188 case "deallocator": 189 case "constructor": 190 ret = parseFunctionDecl(json, parent); 191 break; 192 case "enum": 193 ret = parseEnumDecl(json, parent); 194 break; 195 case "enum member": 196 ret = parseEnumMemberDecl(json, parent); 197 break; 198 case "struct": 199 case "union": 200 case "class": 201 case "interface": 202 ret = parseCompositeDecl(json, parent); 203 break; 204 case "variable": 205 ret = parseVariableDecl(json, parent); 206 break; 207 case "template": 208 ret = parseTemplateDecl(json, parent); 209 break; 210 } 211 } 212 213 ret.protection = parseProtection(json["protection"]); 214 ret.line = json["line"].opt!int; 215 ret.docGroup = new DocGroup(ret, json["comment"].opt!string()); 216 217 m_declarations ~= tuple(ret, json); 218 219 return ret; 220 } 221 222 auto parseAliasDecl(Json json, Entity parent) 223 { 224 auto ret = new AliasDeclaration(parent, json["name"].get!string); 225 ret.attributes = json["storageClass"].opt!(Json[]).map!(j => CachedString(j.get!string)).array.assumeUnique; 226 if( ret.targetType && ret.targetType.kind == TypeKind.Primitive && ret.targetType.typeName.length == 0 ) 227 ret.targetType = CachedType.init; 228 insertIntoTypeMap(ret); 229 return ret; 230 } 231 232 auto parseFunctionDecl(Json json, Entity parent) 233 { 234 auto ret = new FunctionDeclaration(parent, json["name"].opt!string); 235 if (auto psc = "storageClass" in json) 236 foreach (sc; *psc) 237 if (!ret.attributes.canFind(sc.get!string)) 238 ret.attributes ~= CachedString(sc.get!string); 239 return ret; 240 } 241 242 auto parseEnumDecl(Json json, Entity parent) 243 { 244 auto ret = new EnumDeclaration(parent, json["name"].get!string); 245 insertIntoTypeMap(ret); 246 if( "base" !in json ){ // FIXME: parse deco instead 247 if( auto pd = "baseDeco" in json ) 248 json["base"] = demanglePrettyType(pd.get!string()); 249 } 250 auto mems = parseDeclList(json["members"], ret); 251 foreach( m; mems ){ 252 auto em = cast(EnumMemberDeclaration)m; 253 assert(em !is null, "Enum containing non-enum-members?"); 254 ret.members ~= em; 255 } 256 return ret; 257 } 258 259 auto parseEnumMemberDecl(Json json, Entity parent) 260 { 261 auto ret = new EnumMemberDeclaration(parent, json["name"].get!string); 262 if (json["value"].opt!string.length) 263 ret.value = parseValue(json["value"].opt!string); 264 return ret; 265 } 266 267 auto parseCompositeDecl(Json json, Entity parent) 268 { 269 CompositeTypeDeclaration ret; 270 switch(json["kind"].get!string){ 271 default: 272 logWarn("Invalid composite decl kind: %s", json["kind"].get!string); 273 return new StructDeclaration(parent, json["name"].get!string); 274 case "struct": 275 ret = new StructDeclaration(parent, json["name"].get!string); 276 break; 277 case "union": 278 ret = new UnionDeclaration(parent, json["name"].get!string); 279 break; 280 case "class": 281 auto clsdecl = new ClassDeclaration(parent, json["name"].get!string); 282 ret = clsdecl; 283 break; 284 case "interface": 285 auto intfdecl = new InterfaceDeclaration(parent, json["name"].get!string); 286 ret = intfdecl; 287 break; 288 } 289 290 insertIntoTypeMap(ret); 291 292 ret.members = parseDeclList(json["members"], ret); 293 294 return ret; 295 } 296 297 Declaration parseVariableDecl(Json json, Entity parent) 298 { 299 if (json["storageClass"].opt!(Json[]).canFind!(j => j.opt!string == "enum")) { 300 auto ret = new EnumMemberDeclaration(parent, json["name"].get!string); 301 if (json["init"].opt!string.length) 302 ret.value = parseValue(json["init"].opt!string); 303 return ret; 304 } else { 305 auto ret = new VariableDeclaration(parent, json["name"].get!string); 306 if (json["init"].opt!string.length) 307 ret.initializer = parseValue(json["init"].opt!string); 308 return ret; 309 } 310 } 311 312 auto parseTemplateDecl(Json json, Entity parent) 313 { 314 auto ret = new TemplateDeclaration(parent, json["name"].get!string); 315 foreach (arg; json["parameters"].opt!(Json[])) { 316 string pname = arg["name"].get!string; 317 string defvalue = arg["defaultValue"].opt!string; 318 bool needs_type_parse = false; 319 320 switch (arg["kind"].get!string) { 321 default: break; 322 case "value": 323 needs_type_parse = true; 324 break; 325 case "alias": 326 pname = "alias " ~ pname; 327 break; 328 case "tuple": 329 pname ~= "..."; 330 break; 331 } 332 333 auto pdecl = new TemplateParameterDeclaration(ret, pname); 334 pdecl.defaultValue = defvalue; 335 ret.templateArgs ~= pdecl; 336 ret.templateConstraint = json["constraint"].opt!string; 337 338 if (needs_type_parse) 339 m_declarations ~= tuple(cast(Declaration)pdecl, arg); 340 } 341 ret.members = parseDeclList(json["members"], ret); 342 return ret; 343 } 344 345 Type parseType(Json json, Entity sc, string def_type = "void", bool warn_if_not_exists = true) 346 out(ret) { assert(!def_type.length || ret != Type.init); } 347 body { 348 string str; 349 if( json.type == Json.Type.Undefined ){ 350 if (warn_if_not_exists) logWarn("No type found for %s.", sc.qualifiedName); 351 str = def_type; 352 } else if (json.type == Json.Type.String) str = json.get!string(); 353 else if (auto pv = "deco" in json) str = demanglePrettyType(pv.get!string()); 354 else if (auto pv = "type" in json) str = fixFunctionType(pv.get!string(), def_type); 355 else if (auto pv = "originalType" in json) str = fixFunctionType(pv.get!string(), def_type); 356 357 if( str.length == 0 ) str = def_type; 358 359 if( !str.length ) return Type.init; 360 361 return parseType(str, sc); 362 } 363 364 Type parseType(string str, Entity sc) 365 out(ret) { assert(ret != Type.init); } 366 body { 367 auto tokens = tokenizeDSource(str); 368 369 logDebug("parse type '%s'", str); 370 try { 371 auto type = parseTypeDecl(tokens, sc); 372 type.text = str; 373 return type; 374 } catch( Exception e ){ 375 logError("Error parsing type '%s': %s", str, e.msg); 376 Type type; 377 type.text = str; 378 type.typeName = str; 379 type.kind = TypeKind.Primitive; 380 return type; 381 } 382 } 383 384 Value parseValue(string str) 385 { 386 auto ret = new Value; 387 //ret.type = ; 388 ret.valueString = str; 389 return ret; 390 } 391 392 Protection parseProtection(Json prot) 393 { 394 switch( prot.opt!string ){ 395 default: return Protection.Public; 396 case "package": return Protection.Package; 397 case "protected": return Protection.Protected; 398 case "private": return Protection.Private; 399 } 400 } 401 402 Type parseTypeDecl(ref string[] tokens, Entity sc) 403 { 404 405 auto ret = parseType(tokens, sc); 406 return ret; 407 } 408 409 Type parseType(ref string[] tokens, Entity sc) 410 { 411 CachedString[] attributes; 412 auto basic_type = parseBasicType(tokens, sc, attributes); 413 basic_type.attributes ~= attributes; 414 return basic_type; 415 } 416 417 Type parseBasicType(ref string[] tokens, Entity sc, out CachedString[] attributes) 418 out(ret) { assert(ret != Type.init); } 419 body { 420 static immutable global_attribute_keywords = ["abstract", "auto", "const", "deprecated", "enum", 421 "extern", "final", "immutable", "inout", "shared", "nothrow", "override", "pure", 422 "__gshared", "scope", "static", "synchronize"]; 423 424 static immutable parameter_attribute_keywords = ["auto", "const", "final", "immutable", "in", "inout", 425 "lazy", "out", "ref", "scope", "shared"]; 426 427 static immutable member_function_attribute_keywords = ["const", "immutable", "inout", "shared", "pure", "nothrow"]; 428 429 430 if( tokens.length > 0 && tokens[0] == "extern" ){ 431 enforce(tokens[1] == "("); 432 enforce(tokens[3] == ")"); 433 attributes ~= CachedString(join(tokens[0 .. 4])); 434 tokens = tokens[4 .. $]; 435 } 436 437 static immutable string[] attribute_keywords = global_attribute_keywords ~ parameter_attribute_keywords ~ member_function_attribute_keywords; 438 /*final switch( sc ){ 439 case DeclScope.Global: attribute_keywords = global_attribute_keywords; break; 440 case DeclScope.Parameter: attribute_keywords = parameter_attribute_keywords; break; 441 case DeclScope.Class: attribute_keywords = member_function_attribute_keywords; break; 442 }*/ 443 444 void parseAttributes(const(string)[] keywords, scope void delegate(CachedString s) del) 445 { 446 while( tokens.length > 0 ){ 447 if( tokens.front == "@" ){ 448 tokens.popFront(); 449 del(CachedString("@"~tokens.front)); 450 tokens.popFront(); 451 } else if( keywords.countUntil(tokens[0]) >= 0 && tokens.length > 1 && tokens[1] != "(" ){ 452 del(CachedString(tokens.front)); 453 tokens.popFront(); 454 } else break; 455 } 456 } 457 458 parseAttributes(attribute_keywords, (k) { attributes ~= k; }); 459 460 461 Type type; 462 static immutable const_modifiers = ["const", "immutable", "shared", "inout"]; 463 if (tokens.length > 2 && tokens[1] == "(" && const_modifiers.countUntil(tokens[0]) >= 0) { 464 auto mod = tokens.front; 465 tokens.popFrontN(2); 466 CachedString[] subattrs; 467 type = parseBasicType(tokens, sc, subattrs); 468 type.modifiers ~= CachedString(mod); 469 type.attributes ~= subattrs; 470 enforce(!tokens.empty && tokens.front == ")", format("Missing ')' for '%s('", mod)); 471 tokens.popFront(); 472 } else if (!tokens.empty && !tokens.front.among("function", "delegate")) { 473 type.kind = TypeKind.Primitive; 474 475 size_t start = 0, end; 476 if( tokens[start] == "." ) start++; 477 for( end = start; end < tokens.length && isIdent(tokens[end]); ){ 478 end++; 479 if( end >= tokens.length || tokens[end] != "." ) 480 break; 481 end++; 482 } 483 484 size_t i = end; 485 486 string type_name, nested_name; 487 if( i == 0 && tokens[0] == "..." ){ 488 type_name = "..."; 489 nested_name = null; 490 } else if( i == 0 && tokens[0] == "(" ){ 491 type_name = "constructor"; 492 nested_name = null; 493 } else { 494 enforce(i > 0, "Expected identifier but got "~tokens.front); 495 auto unqualified_name = tokens[end - 1]; 496 type.typeName = join(tokens[start .. end]); 497 // 498 tokens.popFrontN(i); 499 500 if (type.typeName == "typeof" && !tokens.empty && tokens.front == "(") { 501 type.typeName ~= "("; 502 tokens.popFront(); 503 int level = 1; 504 while (!tokens.empty && level > 0) { 505 if (tokens.front == "(") level++; 506 else if( tokens.front == ")") level--; 507 type.typeName ~= tokens.front; 508 tokens.popFront(); 509 } 510 } else if( !tokens.empty && tokens.front == "!" ){ 511 tokens.popFront(); 512 if( tokens.front == "(" ){ 513 size_t j = 1; 514 int cc = 1; 515 while( cc > 0 ){ 516 assert(j < tokens.length); 517 if( tokens[j] == "(" ) cc++; 518 else if( tokens[j] == ")") cc--; 519 j++; 520 } 521 type.templateArgs = join(tokens[0 .. j]); 522 tokens.popFrontN(j); 523 logDebug("templargs: %s", type.templateArgs); 524 } else { 525 type.templateArgs = tokens[0]; 526 tokens.popFront(); 527 } 528 529 // resolve eponymous template member, e.g. test.Foo!int.Foo 530 if (!tokens.empty && tokens.front == ".") { 531 tokens.popFront(); 532 if (!tokens.empty && tokens.front == unqualified_name) { // eponymous template 533 resolveTypeDecl(type, sc); 534 auto tdecl = cast(TemplateDeclaration)type.typeDecl; 535 auto members = tdecl ? tdecl.members : null; 536 auto mi = members.countUntil!(m => m.name == tokens.front); 537 assert(mi >= 0 || members.empty); 538 if (mi >= 0) 539 type.typeDecl = members[mi]; 540 tokens.popFront(); 541 } 542 } 543 // HACK: dropping the actual type name here! 544 // TODO: resolve other members and adjust typeName, 545 // e.g. test.Foo!int.Enum, test.Foo!int.Bar!int, test.Foo!int.Struct.Templ!(int, double) 546 while (!tokens.empty && tokens.front == ".") { 547 tokens.popFront(); 548 if (!tokens.empty()) tokens.popFront(); 549 } 550 } 551 552 resolveTypeDecl(type, sc); 553 } 554 } 555 556 while( !tokens.empty ){ 557 if( tokens.front == "*" ){ 558 Type ptr; 559 ptr.kind = TypeKind.Pointer; 560 ptr.elementType = type; 561 type = ptr; 562 tokens.popFront(); 563 } else if( tokens.front == "[" ){ 564 tokens.popFront(); 565 enforce(!tokens.empty, "Missing ']'."); 566 if( tokens.front == "]" ){ 567 Type arr; 568 arr.kind = TypeKind.Array; 569 arr.elementType = type; 570 type = arr; 571 } else { 572 string[] tokens_copy = tokens; 573 Type keytp; 574 if (!isDigit(tokens.front[0]) && tokens.front != "!") keytp = parseType(tokens_copy, sc); 575 if (keytp != Type.init && !tokens_copy.empty && tokens_copy.front == "]") { 576 tokens = tokens_copy; 577 Type aa; 578 aa.kind = TypeKind.AssociativeArray; 579 aa.elementType = type; 580 aa.keyType = keytp; 581 type = aa; 582 } else { 583 Type arr; 584 arr.kind = TypeKind.StaticArray; 585 arr.elementType = type; 586 arr.arrayLength = tokens.front; 587 tokens.popFront(); 588 while (!tokens.empty && tokens.front != "]") { 589 arr.arrayLength ~= tokens.front; 590 tokens.popFront(); 591 } 592 type = arr; 593 } 594 } 595 enforce(tokens.front == "]", "Expected ']', got '"~tokens.front~"'."); 596 tokens.popFront(); 597 } else break; 598 } 599 600 if (type == Type.init) { 601 type.kind = TypeKind.Primitive; 602 type.typeName = "auto"; 603 } 604 605 while (!tokens.empty && (tokens.front == "function" || tokens.front == "delegate" || tokens.front == "(")) { 606 Type ftype; 607 ftype.kind = tokens.front == "(" || tokens.front == "function" ? TypeKind.Function : TypeKind.Delegate; 608 ftype.returnType = type; 609 if (tokens.front != "(") tokens.popFront(); 610 enforce(tokens.front == "("); 611 tokens.popFront(); 612 if (!tokens.empty && tokens.front == ",") tokens.popFront(); // sometimes demangleType() returns something like "void(, ...)" 613 while (true) { 614 if (tokens.front == ")") break; 615 enforce(!tokens.empty); 616 ftype.parameterTypes ~= CachedType(parseTypeDecl(tokens, sc)); 617 string pname; 618 if (tokens.front != "," && tokens.front != ")") { 619 pname = tokens.front; 620 tokens.popFront(); 621 } 622 if (tokens.front == "...") { 623 pname ~= tokens.front; 624 tokens.popFront(); 625 } 626 ftype._parameterNames ~= CachedString(pname); 627 if (tokens.front == "=") { 628 tokens.popFront(); 629 string defval; 630 int ccount = 0; 631 while (!tokens.empty) { 632 if (ccount == 0 && (tokens.front == "," || tokens.front == ")")) 633 break; 634 if (tokens.front == "(") ccount++; 635 else if (tokens.front == ")") ccount--; 636 defval ~= tokens.front; 637 tokens.popFront(); 638 } 639 ftype._parameterDefaultValues ~= cast(immutable)parseValue(defval); 640 logDebug("got defval %s", defval); 641 } else ftype._parameterDefaultValues ~= null; 642 if (tokens.front == ")") break; 643 enforce(tokens.front == ",", "Expecting ',', got "~tokens.front); 644 tokens.popFront(); 645 } 646 tokens.popFront(); 647 648 parseAttributes(member_function_attribute_keywords, (k) { ftype.attributes ~= cast(immutable)k; }); 649 650 type = ftype; 651 } 652 653 return type; 654 } 655 656 /* special function that looks at the default type to see if a function type 657 is expected and if that's the case, fixes up the type string to read 658 as a valid D type declaration (DMD omits "function"/"delegate", which 659 results in an ambiguous meaning) 660 */ 661 private string fixFunctionType(string type, string deftype) 662 { 663 Type dt = parseType(deftype, new Module(null, "dummy")); 664 if (deftype == "void()" || dt != Type.init && dt.kind.among(TypeKind.Function, TypeKind.Delegate)) { 665 auto last_clamp = type.lastIndexOf(')'); 666 auto idx = last_clamp-1; 667 int l = 1; 668 while (idx >= 0) { 669 if (type[idx] == ')') l++; 670 else if (type[idx] == '(') l--; 671 if (l == 0) break; 672 idx--; 673 } 674 if (idx <= 0 || l > 0) return type; 675 return type[0 .. idx] ~ " function" ~ type[idx .. $]; 676 } 677 return type; 678 } 679 680 string[] tokenizeDSource(string dsource) 681 { 682 static import std.uni; 683 import std.utf : stride; 684 685 static immutable string[] tokens = [ 686 "/", "/=", ".", "..", "...", "&", "&=", "&&", "|", "|=", "||", 687 "-", "-=", "--", "+", "+=", "++", "<", "<=", "<<", "<<=", 688 "<>", "<>=", ">", ">=", ">>=", ">>>=", ">>", ">>>", "!", "!=", 689 "!<>", "!<>=", "!<", "!<=", "!>", "!>=", "(", ")", "[", "]", 690 "{", "}", "?", ",", ";", ":", "$", "=", "==", "*", "*=", 691 "%", "%=", "^", "^=", "~", "~=", "@", "=>", "#", "C++" 692 ]; 693 static bool[string] token_map; 694 695 if (token_map is null) { 696 foreach (t; tokens) 697 token_map[t] = true; 698 token_map.rehash; 699 } 700 701 string[] ret; 702 outer: 703 while(true){ 704 dsource = stripLeft(dsource); 705 if( dsource.length == 0 ) break; 706 707 // special token? 708 foreach_reverse (i; 1 .. min(5, dsource.length+1)) 709 if (dsource[0 .. i] in token_map) { 710 ret ~= dsource[0 .. i]; 711 dsource = dsource[i .. $]; 712 continue outer; 713 } 714 715 // identifier? 716 if( dsource[0] == '_' || std.uni.isAlpha(dsource.front) ){ 717 size_t i = 1; 718 string rem = dsource; 719 rem.popFront(); 720 while (rem.length && (rem[0] == '_' || std.uni.isAlpha(rem.front) || isDigit(rem.front))) 721 rem.popFront(); 722 ret ~= dsource[0 .. $ - rem.length]; 723 dsource = rem; 724 continue; 725 } 726 727 // character literal? 728 if( dsource[0] == '\'' ){ 729 size_t i = 1; 730 while( dsource[i] != '\'' ){ 731 if( dsource[i] == '\\' ) i++; 732 i++; 733 enforce(i < dsource.length); 734 } 735 ret ~= dsource[0 .. i+1]; 736 dsource = dsource[i+1 .. $]; 737 continue; 738 } 739 740 // string? (incomplete!) 741 if( dsource[0] == '"' ){ 742 size_t i = 1; 743 while( dsource[i] != '"' ){ 744 if( dsource[i] == '\\' ) i++; 745 i++; 746 enforce(i < dsource.length); 747 } 748 ret ~= dsource[0 .. i+1]; 749 dsource = dsource[i+1 .. $]; 750 continue; 751 } 752 753 // number? 754 if( isDigit(dsource[0]) || dsource[0] == '.' ){ 755 auto dscopy = dsource; 756 parse!double(dscopy); 757 ret ~= dsource[0 .. dsource.length-dscopy.length]; 758 dsource = dscopy; 759 if (dsource.startsWith("u")) dsource.popFront(); 760 else if (dsource.startsWith("f")) dsource.popFront(); 761 continue; 762 } 763 764 auto nb = dsource.stride(); 765 ret ~= dsource[0 .. nb]; 766 dsource = dsource[nb .. $]; 767 } 768 769 return ret; 770 } 771 772 bool isDigit(dchar ch) 773 { 774 return ch >= '0' && ch <= '9'; 775 } 776 777 bool isIdent(string str) 778 { 779 static import std.uni; 780 781 if( str.length < 1 ) return false; 782 foreach( i, dchar ch; str ){ 783 if( ch == '_' || std.uni.isAlpha(ch) ) continue; 784 if( i > 0 && isDigit(ch) ) continue; 785 return false; 786 } 787 return true; 788 } 789 790 string fullStrip(string s) 791 { 792 string chars = " \t\r\n"; 793 while( s.length > 0 && chars.countUntil(s[0]) >= 0 ) s.popFront(); 794 while( s.length > 0 && chars.countUntil(s[$-1]) >= 0 ) s.popBack(); 795 return s; 796 } 797 798 void insertIntoTypeMap(Declaration decl) 799 { 800 auto qname = decl.qualifiedName.to!string; 801 m_typeMap[qname] = decl; 802 auto idx = qname.indexOf('.'); 803 while (idx >= 0) { 804 qname = qname[idx+1 .. $]; 805 m_typeMap[qname] = decl; 806 idx = qname.indexOf('.'); 807 } 808 } 809 810 private void resolveTypeDecl(ref Type tp, const(Entity) sc) 811 { 812 if (tp.kind != TypeKind.Primitive) return; 813 if (tp.typeDecl) return; 814 815 tp.typeDecl = sc.lookup!Declaration(tp.typeName); 816 if (!tp.typeDecl || !isTypeDecl(tp.typeDecl)) tp.typeDecl = m_typeMap.get(tp.typeName, null); 817 } 818 819 private bool isTypeDecl(in Declaration a) 820 { 821 switch(a.kind){ 822 default: return false; 823 case DeclarationKind.Struct: 824 case DeclarationKind.Union: 825 case DeclarationKind.Class: 826 case DeclarationKind.Interface: 827 case DeclarationKind.Enum: 828 return true; 829 case DeclarationKind.Alias: 830 return !!(cast(AliasDeclaration)a).targetType; 831 case DeclarationKind.TemplateParameter: 832 return true; 833 case DeclarationKind.Template: 834 // support eponymous template types 835 auto td = cast(TemplateDeclaration)a; 836 // be optimistic for templates without content that they are in fact types 837 if (!td.members.length) return true; 838 // otherwise require an actual eponymous type member 839 auto mi = td.members.countUntil!(m => m.name == a.name); 840 return mi >= 0 && isTypeDecl(td.members[mi]); 841 } 842 } 843 } 844 845 string demanglePrettyType(string mangled_type) 846 { 847 if (mangled_type == "n") return "typeof(null)"; // Workaround D issue 14410 848 auto str = assumeUnique(demangleType(mangled_type)); 849 str = str.replace("immutable(char)[]", "string"); 850 str = str.replace("immutable(wchar)[]", "wstring"); 851 str = str.replace("immutable(dchar)[]", "dstring"); 852 return str; 853 }