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 string specvalue = arg["specValue"].opt!string; 319 bool needs_type_parse = false; 320 321 switch (arg["kind"].get!string) { 322 default: break; 323 case "value": 324 needs_type_parse = true; 325 break; 326 case "alias": 327 pname = "alias " ~ pname; 328 break; 329 case "tuple": 330 pname ~= "..."; 331 break; 332 } 333 334 auto pdecl = new TemplateParameterDeclaration(ret, pname); 335 pdecl.defaultValue = defvalue; 336 pdecl.specValue = specvalue; 337 ret.templateArgs ~= pdecl; 338 ret.templateConstraint = json["constraint"].opt!string; 339 340 if (needs_type_parse) 341 m_declarations ~= tuple(cast(Declaration)pdecl, arg); 342 } 343 ret.members = parseDeclList(json["members"], ret); 344 return ret; 345 } 346 347 Type parseType(Json json, Entity sc, string def_type = "void", bool warn_if_not_exists = true) 348 out(ret) { assert(!def_type.length || ret != Type.init); } 349 body { 350 string str; 351 if( json.type == Json.Type.Undefined ){ 352 if (warn_if_not_exists) logWarn("No type found for %s.", sc.qualifiedName); 353 str = def_type; 354 } else if (json.type == Json.Type.String) str = json.get!string(); 355 else if (auto pv = "deco" in json) str = demanglePrettyType(pv.get!string()); 356 else if (auto pv = "type" in json) str = fixFunctionType(pv.get!string(), def_type); 357 else if (auto pv = "originalType" in json) str = fixFunctionType(pv.get!string(), def_type); 358 359 if( str.length == 0 ) str = def_type; 360 361 if( !str.length ) return Type.init; 362 363 return parseType(str, sc); 364 } 365 366 Type parseType(string str, Entity sc) 367 out(ret) { assert(ret != Type.init); } 368 body { 369 auto tokens = tokenizeDSource(str); 370 371 logDebug("parse type '%s'", str); 372 try { 373 auto type = parseTypeDecl(tokens, sc); 374 type.text = str; 375 return type; 376 } catch( Exception e ){ 377 logError("Error parsing type '%s': %s", str, e.msg); 378 Type type; 379 type.text = str; 380 type.typeName = str; 381 type.kind = TypeKind.Primitive; 382 return type; 383 } 384 } 385 386 Value parseValue(string str) 387 { 388 auto ret = new Value; 389 //ret.type = ; 390 ret.valueString = str; 391 return ret; 392 } 393 394 Protection parseProtection(Json prot) 395 { 396 switch( prot.opt!string ){ 397 default: return Protection.Public; 398 case "package": return Protection.Package; 399 case "protected": return Protection.Protected; 400 case "private": return Protection.Private; 401 } 402 } 403 404 Type parseTypeDecl(ref string[] tokens, Entity sc) 405 { 406 407 auto ret = parseType(tokens, sc); 408 return ret; 409 } 410 411 Type parseType(ref string[] tokens, Entity sc) 412 { 413 CachedString[] attributes; 414 auto basic_type = parseBasicType(tokens, sc, attributes); 415 basic_type.attributes ~= attributes; 416 return basic_type; 417 } 418 419 Type parseBasicType(ref string[] tokens, Entity sc, out CachedString[] attributes) 420 out(ret) { assert(ret != Type.init); } 421 body { 422 static immutable global_attribute_keywords = ["abstract", "auto", "const", "deprecated", "enum", 423 "extern", "final", "immutable", "inout", "shared", "nothrow", "override", "pure", 424 "__gshared", "scope", "static", "synchronize"]; 425 426 static immutable parameter_attribute_keywords = ["auto", "const", "final", "immutable", "in", "inout", 427 "lazy", "out", "ref", "return", "scope", "shared"]; 428 429 static immutable member_function_attribute_keywords = ["const", "immutable", "inout", "ref", "return", 430 "scope", "shared", "pure", "nothrow"]; 431 432 433 if( tokens.length > 0 && tokens[0] == "extern" ){ 434 enforce(tokens[1] == "("); 435 enforce(tokens[3] == ")"); 436 attributes ~= CachedString(join(tokens[0 .. 4])); 437 tokens = tokens[4 .. $]; 438 } 439 440 static immutable string[] attribute_keywords = global_attribute_keywords ~ parameter_attribute_keywords ~ member_function_attribute_keywords; 441 /*final switch( sc ){ 442 case DeclScope.Global: attribute_keywords = global_attribute_keywords; break; 443 case DeclScope.Parameter: attribute_keywords = parameter_attribute_keywords; break; 444 case DeclScope.Class: attribute_keywords = member_function_attribute_keywords; break; 445 }*/ 446 447 void parseAttributes(const(string)[] keywords, scope void delegate(CachedString s) del) 448 { 449 while( tokens.length > 0 ){ 450 if( tokens.front == "@" ){ 451 tokens.popFront(); 452 del(CachedString("@"~tokens.front)); 453 tokens.popFront(); 454 } else if( keywords.countUntil(tokens[0]) >= 0 && tokens.length > 1 && tokens[1] != "(" ){ 455 del(CachedString(tokens.front)); 456 tokens.popFront(); 457 } else break; 458 } 459 } 460 461 parseAttributes(attribute_keywords, (k) { attributes ~= k; }); 462 463 464 Type type; 465 static immutable const_modifiers = ["const", "immutable", "shared", "inout"]; 466 if (tokens.length > 2 && tokens[1] == "(" && const_modifiers.countUntil(tokens[0]) >= 0) { 467 auto mod = tokens.front; 468 tokens.popFrontN(2); 469 CachedString[] subattrs; 470 type = parseBasicType(tokens, sc, subattrs); 471 type.modifiers ~= CachedString(mod); 472 type.attributes ~= subattrs; 473 enforce(!tokens.empty && tokens.front == ")", format("Missing ')' for '%s('", mod)); 474 tokens.popFront(); 475 } else if (!tokens.empty && !tokens.front.among("function", "delegate")) { 476 type.kind = TypeKind.Primitive; 477 478 size_t start = 0, end; 479 if( tokens[start] == "." ) start++; 480 for( end = start; end < tokens.length && isIdent(tokens[end]); ){ 481 end++; 482 if( end >= tokens.length || tokens[end] != "." ) 483 break; 484 end++; 485 } 486 487 size_t i = end; 488 489 string type_name, nested_name; 490 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 // demangleType() returns something like "void(, ...)" for variadic functions or "void(, type)" for typeof(null) parameters 613 if (!tokens.empty && tokens.front == ",") tokens.popFront(); 614 // (...) - D variadic function 615 if (tokens.front == "...") { 616 ftype.variadic = Type.Variadic.d; 617 tokens.popFront(); 618 } 619 while (true) { 620 if (tokens.front == ")") break; 621 622 // (int) - parameter type 623 enforce(!tokens.empty); 624 ftype.parameterTypes ~= CachedType(parseTypeDecl(tokens, sc)); 625 626 627 // (int[]...), (Clazz...) - typesafe variadic function 628 if (tokens.front == "...") { 629 ftype.variadic = Type.Variadic.typesafe; 630 tokens.popFront(); 631 } 632 // (type, ...) - D or extern(C) variadic 633 else if (tokens.length > 2 && tokens[0] == "," && tokens[1] == "...") { 634 ftype.variadic = Type.Variadic.c; // c and d treated identical for doc-gen 635 tokens.popFrontN(2); 636 } 637 638 string pname; 639 if (tokens.front != "," && tokens.front != ")") { 640 pname = tokens.front; 641 tokens.popFront(); 642 } 643 ftype._parameterNames ~= CachedString(pname); 644 if (tokens.front == "=") { 645 tokens.popFront(); 646 string defval; 647 int ccount = 0; 648 while (!tokens.empty) { 649 if (ccount == 0 && (tokens.front == "," || tokens.front == ")")) 650 break; 651 if (tokens.front == "(") ccount++; 652 else if (tokens.front == ")") ccount--; 653 defval ~= tokens.front; 654 tokens.popFront(); 655 } 656 ftype._parameterDefaultValues ~= cast(immutable)parseValue(defval); 657 logDebug("got defval %s", defval); 658 } else ftype._parameterDefaultValues ~= null; 659 if (tokens.front == ")") break; 660 enforce(tokens.front == ",", "Expecting ',', got "~tokens.front); 661 tokens.popFront(); 662 } 663 tokens.popFront(); 664 665 parseAttributes(member_function_attribute_keywords, (k) { ftype.attributes ~= cast(immutable)k; }); 666 667 type = ftype; 668 } 669 670 return type; 671 } 672 673 /* special function that looks at the default type to see if a function type 674 is expected and if that's the case, fixes up the type string to read 675 as a valid D type declaration (DMD omits "function"/"delegate", which 676 results in an ambiguous meaning) 677 */ 678 private string fixFunctionType(string type, string deftype) 679 { 680 Type dt = parseType(deftype, new Module(null, "dummy")); 681 if (deftype == "void()" || dt != Type.init && dt.kind.among(TypeKind.Function, TypeKind.Delegate)) { 682 auto last_clamp = type.lastIndexOf(')'); 683 auto idx = last_clamp-1; 684 int l = 1; 685 while (idx >= 0) { 686 if (type[idx] == ')') l++; 687 else if (type[idx] == '(') l--; 688 if (l == 0) break; 689 idx--; 690 } 691 if (idx <= 0 || l > 0) return type; 692 return type[0 .. idx] ~ " function" ~ type[idx .. $]; 693 } 694 return type; 695 } 696 697 string[] tokenizeDSource(string dsource) 698 { 699 static import std.uni; 700 import std.utf : stride; 701 702 static immutable string[] tokens = [ 703 "/", "/=", ".", "..", "...", "&", "&=", "&&", "|", "|=", "||", 704 "-", "-=", "--", "+", "+=", "++", "<", "<=", "<<", "<<=", 705 "<>", "<>=", ">", ">=", ">>=", ">>>=", ">>", ">>>", "!", "!=", 706 "!<>", "!<>=", "!<", "!<=", "!>", "!>=", "(", ")", "[", "]", 707 "{", "}", "?", ",", ";", ":", "$", "=", "==", "*", "*=", 708 "%", "%=", "^", "^=", "~", "~=", "@", "=>", "#", "C++" 709 ]; 710 static bool[string] token_map; 711 712 if (token_map is null) { 713 foreach (t; tokens) 714 token_map[t] = true; 715 token_map.rehash; 716 } 717 718 string[] ret; 719 outer: 720 while(true){ 721 dsource = stripLeft(dsource); 722 if( dsource.length == 0 ) break; 723 724 // special token? 725 foreach_reverse (i; 1 .. min(5, dsource.length+1)) 726 if (dsource[0 .. i] in token_map) { 727 ret ~= dsource[0 .. i]; 728 dsource = dsource[i .. $]; 729 continue outer; 730 } 731 732 // identifier? 733 if( dsource[0] == '_' || std.uni.isAlpha(dsource.front) ){ 734 size_t i = 1; 735 string rem = dsource; 736 rem.popFront(); 737 while (rem.length && (rem[0] == '_' || std.uni.isAlpha(rem.front) || isDigit(rem.front))) 738 rem.popFront(); 739 ret ~= dsource[0 .. $ - rem.length]; 740 dsource = rem; 741 continue; 742 } 743 744 // character literal? 745 if( dsource[0] == '\'' ){ 746 size_t i = 1; 747 while( dsource[i] != '\'' ){ 748 if( dsource[i] == '\\' ) i++; 749 i++; 750 enforce(i < dsource.length); 751 } 752 ret ~= dsource[0 .. i+1]; 753 dsource = dsource[i+1 .. $]; 754 continue; 755 } 756 757 // string? (incomplete!) 758 if( dsource[0] == '"' ){ 759 size_t i = 1; 760 while( dsource[i] != '"' ){ 761 if( dsource[i] == '\\' ) i++; 762 i++; 763 enforce(i < dsource.length); 764 } 765 ret ~= dsource[0 .. i+1]; 766 dsource = dsource[i+1 .. $]; 767 continue; 768 } 769 770 // number? 771 if( isDigit(dsource[0]) || dsource[0] == '.' ){ 772 auto dscopy = dsource; 773 parse!double(dscopy); 774 ret ~= dsource[0 .. dsource.length-dscopy.length]; 775 dsource = dscopy; 776 if (dsource.startsWith("u")) dsource.popFront(); 777 else if (dsource.startsWith("f")) dsource.popFront(); 778 continue; 779 } 780 781 auto nb = dsource.stride(); 782 ret ~= dsource[0 .. nb]; 783 dsource = dsource[nb .. $]; 784 } 785 786 return ret; 787 } 788 789 bool isDigit(dchar ch) 790 { 791 return ch >= '0' && ch <= '9'; 792 } 793 794 bool isIdent(string str) 795 { 796 static import std.uni; 797 798 if( str.length < 1 ) return false; 799 foreach( i, dchar ch; str ){ 800 if( ch == '_' || std.uni.isAlpha(ch) ) continue; 801 if( i > 0 && isDigit(ch) ) continue; 802 return false; 803 } 804 return true; 805 } 806 807 string fullStrip(string s) 808 { 809 string chars = " \t\r\n"; 810 while( s.length > 0 && chars.countUntil(s[0]) >= 0 ) s.popFront(); 811 while( s.length > 0 && chars.countUntil(s[$-1]) >= 0 ) s.popBack(); 812 return s; 813 } 814 815 void insertIntoTypeMap(Declaration decl) 816 { 817 auto qname = decl.qualifiedName.to!string; 818 m_typeMap[qname] = decl; 819 auto idx = qname.indexOf('.'); 820 while (idx >= 0) { 821 qname = qname[idx+1 .. $]; 822 m_typeMap[qname] = decl; 823 idx = qname.indexOf('.'); 824 } 825 } 826 827 private void resolveTypeDecl(ref Type tp, const(Entity) sc) 828 { 829 if (tp.kind != TypeKind.Primitive) return; 830 if (tp.typeDecl) return; 831 832 tp.typeDecl = sc.lookup!Declaration(tp.typeName); 833 if (!tp.typeDecl || !isTypeDecl(tp.typeDecl)) tp.typeDecl = m_typeMap.get(tp.typeName, null); 834 } 835 836 private bool isTypeDecl(in Declaration a) 837 { 838 switch(a.kind){ 839 default: return false; 840 case DeclarationKind.Struct: 841 case DeclarationKind.Union: 842 case DeclarationKind.Class: 843 case DeclarationKind.Interface: 844 case DeclarationKind.Enum: 845 return true; 846 case DeclarationKind.Alias: 847 return !!(cast(AliasDeclaration)a).targetType; 848 case DeclarationKind.TemplateParameter: 849 return true; 850 case DeclarationKind.Template: 851 // support eponymous template types 852 auto td = cast(TemplateDeclaration)a; 853 // be optimistic for templates without content that they are in fact types 854 if (!td.members.length) return true; 855 // otherwise require an actual eponymous type member 856 auto mi = td.members.countUntil!(m => m.name == a.name); 857 return mi >= 0 && isTypeDecl(td.members[mi]); 858 } 859 } 860 } 861 862 string demanglePrettyType(string mangled_type) 863 { 864 if (mangled_type == "n") return "typeof(null)"; // Workaround D issue 14410 865 auto str = assumeUnique(demangleType(mangled_type)); 866 str = str.replace("immutable(char)[]", "string"); 867 str = str.replace("immutable(wchar)[]", "wstring"); 868 str = str.replace("immutable(dchar)[]", "dstring"); 869 return str; 870 }