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", "return", "scope", "shared"]; 426 427 static immutable member_function_attribute_keywords = ["const", "immutable", "inout", "ref", "return", 428 "scope", "shared", "pure", "nothrow"]; 429 430 431 if( tokens.length > 0 && tokens[0] == "extern" ){ 432 enforce(tokens[1] == "("); 433 enforce(tokens[3] == ")"); 434 attributes ~= CachedString(join(tokens[0 .. 4])); 435 tokens = tokens[4 .. $]; 436 } 437 438 static immutable string[] attribute_keywords = global_attribute_keywords ~ parameter_attribute_keywords ~ member_function_attribute_keywords; 439 /*final switch( sc ){ 440 case DeclScope.Global: attribute_keywords = global_attribute_keywords; break; 441 case DeclScope.Parameter: attribute_keywords = parameter_attribute_keywords; break; 442 case DeclScope.Class: attribute_keywords = member_function_attribute_keywords; break; 443 }*/ 444 445 void parseAttributes(const(string)[] keywords, scope void delegate(CachedString s) del) 446 { 447 while( tokens.length > 0 ){ 448 if( tokens.front == "@" ){ 449 tokens.popFront(); 450 del(CachedString("@"~tokens.front)); 451 tokens.popFront(); 452 } else if( keywords.countUntil(tokens[0]) >= 0 && tokens.length > 1 && tokens[1] != "(" ){ 453 del(CachedString(tokens.front)); 454 tokens.popFront(); 455 } else break; 456 } 457 } 458 459 parseAttributes(attribute_keywords, (k) { attributes ~= k; }); 460 461 462 Type type; 463 static immutable const_modifiers = ["const", "immutable", "shared", "inout"]; 464 if (tokens.length > 2 && tokens[1] == "(" && const_modifiers.countUntil(tokens[0]) >= 0) { 465 auto mod = tokens.front; 466 tokens.popFrontN(2); 467 CachedString[] subattrs; 468 type = parseBasicType(tokens, sc, subattrs); 469 type.modifiers ~= CachedString(mod); 470 type.attributes ~= subattrs; 471 enforce(!tokens.empty && tokens.front == ")", format("Missing ')' for '%s('", mod)); 472 tokens.popFront(); 473 } else if (!tokens.empty && !tokens.front.among("function", "delegate")) { 474 type.kind = TypeKind.Primitive; 475 476 size_t start = 0, end; 477 if( tokens[start] == "." ) start++; 478 for( end = start; end < tokens.length && isIdent(tokens[end]); ){ 479 end++; 480 if( end >= tokens.length || tokens[end] != "." ) 481 break; 482 end++; 483 } 484 485 size_t i = end; 486 487 string type_name, nested_name; 488 if( i == 0 && tokens[0] == "(" ){ 489 type_name = "constructor"; 490 nested_name = null; 491 } else { 492 enforce(i > 0, "Expected identifier but got "~tokens.front); 493 auto unqualified_name = tokens[end - 1]; 494 type.typeName = join(tokens[start .. end]); 495 // 496 tokens.popFrontN(i); 497 498 if (type.typeName == "typeof" && !tokens.empty && tokens.front == "(") { 499 type.typeName ~= "("; 500 tokens.popFront(); 501 int level = 1; 502 while (!tokens.empty && level > 0) { 503 if (tokens.front == "(") level++; 504 else if( tokens.front == ")") level--; 505 type.typeName ~= tokens.front; 506 tokens.popFront(); 507 } 508 } else if( !tokens.empty && tokens.front == "!" ){ 509 tokens.popFront(); 510 if( tokens.front == "(" ){ 511 size_t j = 1; 512 int cc = 1; 513 while( cc > 0 ){ 514 assert(j < tokens.length); 515 if( tokens[j] == "(" ) cc++; 516 else if( tokens[j] == ")") cc--; 517 j++; 518 } 519 type.templateArgs = join(tokens[0 .. j]); 520 tokens.popFrontN(j); 521 logDebug("templargs: %s", type.templateArgs); 522 } else { 523 type.templateArgs = tokens[0]; 524 tokens.popFront(); 525 } 526 527 // resolve eponymous template member, e.g. test.Foo!int.Foo 528 if (!tokens.empty && tokens.front == ".") { 529 tokens.popFront(); 530 if (!tokens.empty && tokens.front == unqualified_name) { // eponymous template 531 resolveTypeDecl(type, sc); 532 auto tdecl = cast(TemplateDeclaration)type.typeDecl; 533 auto members = tdecl ? tdecl.members : null; 534 auto mi = members.countUntil!(m => m.name == tokens.front); 535 assert(mi >= 0 || members.empty); 536 if (mi >= 0) 537 type.typeDecl = members[mi]; 538 tokens.popFront(); 539 } 540 } 541 // HACK: dropping the actual type name here! 542 // TODO: resolve other members and adjust typeName, 543 // e.g. test.Foo!int.Enum, test.Foo!int.Bar!int, test.Foo!int.Struct.Templ!(int, double) 544 while (!tokens.empty && tokens.front == ".") { 545 tokens.popFront(); 546 if (!tokens.empty()) tokens.popFront(); 547 } 548 } 549 550 resolveTypeDecl(type, sc); 551 } 552 } 553 554 while( !tokens.empty ){ 555 if( tokens.front == "*" ){ 556 Type ptr; 557 ptr.kind = TypeKind.Pointer; 558 ptr.elementType = type; 559 type = ptr; 560 tokens.popFront(); 561 } else if( tokens.front == "[" ){ 562 tokens.popFront(); 563 enforce(!tokens.empty, "Missing ']'."); 564 if( tokens.front == "]" ){ 565 Type arr; 566 arr.kind = TypeKind.Array; 567 arr.elementType = type; 568 type = arr; 569 } else { 570 string[] tokens_copy = tokens; 571 Type keytp; 572 if (!isDigit(tokens.front[0]) && tokens.front != "!") keytp = parseType(tokens_copy, sc); 573 if (keytp != Type.init && !tokens_copy.empty && tokens_copy.front == "]") { 574 tokens = tokens_copy; 575 Type aa; 576 aa.kind = TypeKind.AssociativeArray; 577 aa.elementType = type; 578 aa.keyType = keytp; 579 type = aa; 580 } else { 581 Type arr; 582 arr.kind = TypeKind.StaticArray; 583 arr.elementType = type; 584 arr.arrayLength = tokens.front; 585 tokens.popFront(); 586 while (!tokens.empty && tokens.front != "]") { 587 arr.arrayLength ~= tokens.front; 588 tokens.popFront(); 589 } 590 type = arr; 591 } 592 } 593 enforce(tokens.front == "]", "Expected ']', got '"~tokens.front~"'."); 594 tokens.popFront(); 595 } else break; 596 } 597 598 if (type == Type.init) { 599 type.kind = TypeKind.Primitive; 600 type.typeName = "auto"; 601 } 602 603 while (!tokens.empty && (tokens.front == "function" || tokens.front == "delegate" || tokens.front == "(")) { 604 Type ftype; 605 ftype.kind = tokens.front == "(" || tokens.front == "function" ? TypeKind.Function : TypeKind.Delegate; 606 ftype.returnType = type; 607 if (tokens.front != "(") tokens.popFront(); 608 enforce(tokens.front == "("); 609 tokens.popFront(); 610 // demangleType() returns something like "void(, ...)" for variadic functions or "void(, type)" for typeof(null) parameters 611 if (!tokens.empty && tokens.front == ",") tokens.popFront(); 612 // (...) - D variadic function 613 if (tokens.front == "...") { 614 ftype.variadic = Type.Variadic.d; 615 tokens.popFront(); 616 } 617 while (true) { 618 if (tokens.front == ")") break; 619 620 // (int) - parameter type 621 enforce(!tokens.empty); 622 ftype.parameterTypes ~= CachedType(parseTypeDecl(tokens, sc)); 623 624 625 // (int[]...), (Clazz...) - typesafe variadic function 626 if (tokens.front == "...") { 627 ftype.variadic = Type.Variadic.typesafe; 628 tokens.popFront(); 629 } 630 // (type, ...) - D or extern(C) variadic 631 else if (tokens.length > 2 && tokens[0] == "," && tokens[1] == "...") { 632 ftype.variadic = Type.Variadic.c; // c and d treated identical for doc-gen 633 tokens.popFrontN(2); 634 } 635 636 string pname; 637 if (tokens.front != "," && tokens.front != ")") { 638 pname = tokens.front; 639 tokens.popFront(); 640 } 641 ftype._parameterNames ~= CachedString(pname); 642 if (tokens.front == "=") { 643 tokens.popFront(); 644 string defval; 645 int ccount = 0; 646 while (!tokens.empty) { 647 if (ccount == 0 && (tokens.front == "," || tokens.front == ")")) 648 break; 649 if (tokens.front == "(") ccount++; 650 else if (tokens.front == ")") ccount--; 651 defval ~= tokens.front; 652 tokens.popFront(); 653 } 654 ftype._parameterDefaultValues ~= cast(immutable)parseValue(defval); 655 logDebug("got defval %s", defval); 656 } else ftype._parameterDefaultValues ~= null; 657 if (tokens.front == ")") break; 658 enforce(tokens.front == ",", "Expecting ',', got "~tokens.front); 659 tokens.popFront(); 660 } 661 tokens.popFront(); 662 663 parseAttributes(member_function_attribute_keywords, (k) { ftype.attributes ~= cast(immutable)k; }); 664 665 type = ftype; 666 } 667 668 return type; 669 } 670 671 /* special function that looks at the default type to see if a function type 672 is expected and if that's the case, fixes up the type string to read 673 as a valid D type declaration (DMD omits "function"/"delegate", which 674 results in an ambiguous meaning) 675 */ 676 private string fixFunctionType(string type, string deftype) 677 { 678 Type dt = parseType(deftype, new Module(null, "dummy")); 679 if (deftype == "void()" || dt != Type.init && dt.kind.among(TypeKind.Function, TypeKind.Delegate)) { 680 auto last_clamp = type.lastIndexOf(')'); 681 auto idx = last_clamp-1; 682 int l = 1; 683 while (idx >= 0) { 684 if (type[idx] == ')') l++; 685 else if (type[idx] == '(') l--; 686 if (l == 0) break; 687 idx--; 688 } 689 if (idx <= 0 || l > 0) return type; 690 return type[0 .. idx] ~ " function" ~ type[idx .. $]; 691 } 692 return type; 693 } 694 695 string[] tokenizeDSource(string dsource) 696 { 697 static import std.uni; 698 import std.utf : stride; 699 700 static immutable string[] tokens = [ 701 "/", "/=", ".", "..", "...", "&", "&=", "&&", "|", "|=", "||", 702 "-", "-=", "--", "+", "+=", "++", "<", "<=", "<<", "<<=", 703 "<>", "<>=", ">", ">=", ">>=", ">>>=", ">>", ">>>", "!", "!=", 704 "!<>", "!<>=", "!<", "!<=", "!>", "!>=", "(", ")", "[", "]", 705 "{", "}", "?", ",", ";", ":", "$", "=", "==", "*", "*=", 706 "%", "%=", "^", "^=", "~", "~=", "@", "=>", "#", "C++" 707 ]; 708 static bool[string] token_map; 709 710 if (token_map is null) { 711 foreach (t; tokens) 712 token_map[t] = true; 713 token_map.rehash; 714 } 715 716 string[] ret; 717 outer: 718 while(true){ 719 dsource = stripLeft(dsource); 720 if( dsource.length == 0 ) break; 721 722 // special token? 723 foreach_reverse (i; 1 .. min(5, dsource.length+1)) 724 if (dsource[0 .. i] in token_map) { 725 ret ~= dsource[0 .. i]; 726 dsource = dsource[i .. $]; 727 continue outer; 728 } 729 730 // identifier? 731 if( dsource[0] == '_' || std.uni.isAlpha(dsource.front) ){ 732 size_t i = 1; 733 string rem = dsource; 734 rem.popFront(); 735 while (rem.length && (rem[0] == '_' || std.uni.isAlpha(rem.front) || isDigit(rem.front))) 736 rem.popFront(); 737 ret ~= dsource[0 .. $ - rem.length]; 738 dsource = rem; 739 continue; 740 } 741 742 // character literal? 743 if( dsource[0] == '\'' ){ 744 size_t i = 1; 745 while( dsource[i] != '\'' ){ 746 if( dsource[i] == '\\' ) i++; 747 i++; 748 enforce(i < dsource.length); 749 } 750 ret ~= dsource[0 .. i+1]; 751 dsource = dsource[i+1 .. $]; 752 continue; 753 } 754 755 // string? (incomplete!) 756 if( dsource[0] == '"' ){ 757 size_t i = 1; 758 while( dsource[i] != '"' ){ 759 if( dsource[i] == '\\' ) i++; 760 i++; 761 enforce(i < dsource.length); 762 } 763 ret ~= dsource[0 .. i+1]; 764 dsource = dsource[i+1 .. $]; 765 continue; 766 } 767 768 // number? 769 if( isDigit(dsource[0]) || dsource[0] == '.' ){ 770 auto dscopy = dsource; 771 parse!double(dscopy); 772 ret ~= dsource[0 .. dsource.length-dscopy.length]; 773 dsource = dscopy; 774 if (dsource.startsWith("u")) dsource.popFront(); 775 else if (dsource.startsWith("f")) dsource.popFront(); 776 continue; 777 } 778 779 auto nb = dsource.stride(); 780 ret ~= dsource[0 .. nb]; 781 dsource = dsource[nb .. $]; 782 } 783 784 return ret; 785 } 786 787 bool isDigit(dchar ch) 788 { 789 return ch >= '0' && ch <= '9'; 790 } 791 792 bool isIdent(string str) 793 { 794 static import std.uni; 795 796 if( str.length < 1 ) return false; 797 foreach( i, dchar ch; str ){ 798 if( ch == '_' || std.uni.isAlpha(ch) ) continue; 799 if( i > 0 && isDigit(ch) ) continue; 800 return false; 801 } 802 return true; 803 } 804 805 string fullStrip(string s) 806 { 807 string chars = " \t\r\n"; 808 while( s.length > 0 && chars.countUntil(s[0]) >= 0 ) s.popFront(); 809 while( s.length > 0 && chars.countUntil(s[$-1]) >= 0 ) s.popBack(); 810 return s; 811 } 812 813 void insertIntoTypeMap(Declaration decl) 814 { 815 auto qname = decl.qualifiedName.to!string; 816 m_typeMap[qname] = decl; 817 auto idx = qname.indexOf('.'); 818 while (idx >= 0) { 819 qname = qname[idx+1 .. $]; 820 m_typeMap[qname] = decl; 821 idx = qname.indexOf('.'); 822 } 823 } 824 825 private void resolveTypeDecl(ref Type tp, const(Entity) sc) 826 { 827 if (tp.kind != TypeKind.Primitive) return; 828 if (tp.typeDecl) return; 829 830 tp.typeDecl = sc.lookup!Declaration(tp.typeName); 831 if (!tp.typeDecl || !isTypeDecl(tp.typeDecl)) tp.typeDecl = m_typeMap.get(tp.typeName, null); 832 } 833 834 private bool isTypeDecl(in Declaration a) 835 { 836 switch(a.kind){ 837 default: return false; 838 case DeclarationKind.Struct: 839 case DeclarationKind.Union: 840 case DeclarationKind.Class: 841 case DeclarationKind.Interface: 842 case DeclarationKind.Enum: 843 return true; 844 case DeclarationKind.Alias: 845 return !!(cast(AliasDeclaration)a).targetType; 846 case DeclarationKind.TemplateParameter: 847 return true; 848 case DeclarationKind.Template: 849 // support eponymous template types 850 auto td = cast(TemplateDeclaration)a; 851 // be optimistic for templates without content that they are in fact types 852 if (!td.members.length) return true; 853 // otherwise require an actual eponymous type member 854 auto mi = td.members.countUntil!(m => m.name == a.name); 855 return mi >= 0 && isTypeDecl(td.members[mi]); 856 } 857 } 858 } 859 860 string demanglePrettyType(string mangled_type) 861 { 862 if (mangled_type == "n") return "typeof(null)"; // Workaround D issue 14410 863 auto str = assumeUnique(demangleType(mangled_type)); 864 str = str.replace("immutable(char)[]", "string"); 865 str = str.replace("immutable(wchar)[]", "wstring"); 866 str = str.replace("immutable(dchar)[]", "dstring"); 867 return str; 868 }