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