1 /** 2 Parses DMD JSON output and builds up a socumentation syntax tree (JSON format up to DMD 2.061). 3 4 Copyright: © 2012 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.jsonparser_old; 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 vibe.core.log; 21 import vibe.data.json; 22 23 24 Package parseJsonDocsOld(Json json, Package root = null) 25 { 26 if( !root ) root = new Package(null, null); 27 Parser p; 28 foreach( mod; json ){ 29 p.parseModule(mod, root); 30 } 31 p.resolveTypes(root); 32 return root; 33 } 34 35 private struct Parser 36 { 37 private Tuple!(Type, Entity)[] m_primTypes; 38 private Declaration[string] m_typeMap; 39 40 void resolveTypes(Package root) 41 { 42 bool isTypeDecl(Declaration a) 43 { 44 switch(a.kind){ 45 default: return false; 46 case DeclarationKind.Struct: 47 case DeclarationKind.Union: 48 case DeclarationKind.Class: 49 case DeclarationKind.Interface: 50 case DeclarationKind.Enum: 51 return true; 52 case DeclarationKind.Alias: 53 return (cast(AliasDeclaration)a).targetType !is null; 54 } 55 } 56 57 foreach( t; m_primTypes ){ 58 auto decl = cast(Declaration)t[1].lookup(t[0].typeName); 59 if( !decl || !isTypeDecl(decl) ){ 60 auto pd = t[0].typeName in m_typeMap; 61 if( pd ) decl = *pd; 62 } 63 if( decl && isTypeDecl(decl) ) 64 t[0].typeDecl = decl; 65 } 66 67 // fixup class bases 68 root.visit!ClassDeclaration((decl){ 69 if( decl.baseClass && decl.baseClass.typeDecl && !cast(ClassDeclaration)decl.baseClass.typeDecl ) 70 decl.baseClass = null; 71 foreach( i; decl.derivedInterfaces ) 72 if( i.typeDecl && !cast(InterfaceDeclaration)i.typeDecl ) 73 i.typeDecl = null; 74 assert(decl); 75 }); 76 77 // fixup interface bases 78 root.visit!InterfaceDeclaration((decl){ 79 foreach( i; decl.derivedInterfaces ) 80 if( i.typeDecl && !cast(InterfaceDeclaration)i.typeDecl ) 81 i.typeDecl = null; 82 assert(decl); 83 }); 84 } 85 86 void parseModule(Json json, Package root_package) 87 { 88 Module mod; 89 if( "name" !in json ){ 90 logError("No name attribute in module %s - ignoring", json.filename.opt!string); 91 return; 92 } 93 auto path = json.name.get!string.split("."); 94 Package p = root_package; 95 foreach( i, pe; path ){ 96 if( i+1 < path.length ) p = p.getOrAddPackage(pe); 97 else mod = p.createModule(pe); 98 } 99 100 mod.file = json.file.get!string; 101 mod.docGroup = new DocGroup(mod, json.comment.opt!string()); 102 mod.members = parseDeclList(json.members, mod); 103 } 104 105 Declaration[] parseDeclList(Json json, Entity parent) 106 { 107 if( json.type == Json.Type.Undefined ) return null; 108 DocGroup lastdoc; 109 Declaration[] ret; 110 foreach( mem; json ){ 111 auto decl = parseDecl(mem, parent); 112 auto doc = decl.docGroup; 113 if( lastdoc && (doc.text == lastdoc.text && doc.text.length || doc.comment.isDitto) ){ 114 lastdoc.members ~= decl; 115 decl.docGroup = lastdoc; 116 } else if( doc.comment.isPrivate ){ 117 decl.protection = Protection.Private; 118 lastdoc = null; 119 } else lastdoc = decl.docGroup; 120 ret ~= decl; 121 } 122 return ret; 123 } 124 125 Declaration parseDecl(Json json, Entity parent) 126 { 127 Declaration ret; 128 129 // DMD outputs templates with the wrong kind sometimes 130 if( json.name.get!string().canFind('(') ){ 131 ret = parseTemplateDecl(json, parent); 132 } else { 133 switch( json.kind.get!string ){ 134 default: enforce(false, "Unknown declaration kind: "~json.kind.get!string); assert(false); 135 case "alias": 136 ret = parseAliasDecl(json, parent); 137 break; 138 case "function": 139 case "allocator": 140 case "deallocator": 141 case "constructor": 142 ret = parseFunctionDecl(json, parent); 143 break; 144 case "enum": 145 ret = parseEnumDecl(json, parent); 146 break; 147 case "enum member": 148 ret = parseEnumMemberDecl(json, parent); 149 break; 150 case "struct": 151 case "union": 152 case "class": 153 case "interface": 154 ret = parseCompositeDecl(json, parent); 155 break; 156 case "variable": 157 ret = parseVariableDecl(json, parent); 158 break; 159 case "template": 160 ret = parseTemplateDecl(json, parent); 161 break; 162 } 163 } 164 165 ret.protection = parseProtection(json.protection); 166 ret.line = json["line"].opt!int; 167 ret.docGroup = new DocGroup(ret, json.comment.opt!string()); 168 169 return ret; 170 } 171 172 auto parseAliasDecl(Json json, Entity parent) 173 { 174 auto ret = new AliasDeclaration(parent, json.name.get!string); 175 if( auto pt = "type" in json ){ 176 ret.targetType = parseType(*pt, parent, null); 177 if( ret.targetType.kind == TypeKind.Primitive && ret.targetType.typeName.length == 0 ) 178 ret.targetType = null; 179 } 180 insertIntoTypeMap(ret); 181 return ret; 182 } 183 184 auto parseFunctionDecl(Json json, Entity parent) 185 { 186 auto ret = new FunctionDeclaration(parent, json.name.get!string); 187 ret.type = parseType(json["type"], parent); 188 if( ret.type.kind == TypeKind.Function ){ 189 ret.returnType = ret.type.returnType; 190 ret.attributes = ret.type.attributes; 191 foreach( i, pt; ret.type.parameterTypes ){ 192 auto decl = new VariableDeclaration(ret, ret.type._parameterNames[i]); 193 decl.type = pt; 194 decl.initializer = ret.type._parameterDefaultValues[i]; 195 ret.parameters ~= decl; 196 } 197 } else { 198 logError("Expected function type for '%s', got %s", json["type"].opt!string, ret.type.kind); 199 } 200 return ret; 201 } 202 203 auto parseEnumDecl(Json json, Entity parent) 204 { 205 auto ret = new EnumDeclaration(parent, json.name.get!string); 206 insertIntoTypeMap(ret); 207 ret.baseType = parseType(json.base, parent); 208 auto mems = parseDeclList(json.members, ret); 209 foreach( m; mems ){ 210 auto em = cast(EnumMemberDeclaration)m; 211 assert(em !is null, "Enum containing non-enum-members?"); 212 ret.members ~= em; 213 } 214 return ret; 215 } 216 217 auto parseEnumMemberDecl(Json json, Entity parent) 218 { 219 auto ret = new EnumMemberDeclaration(parent, json.name.get!string); 220 //ret.value = parseValue(json.value); 221 return ret; 222 } 223 224 auto parseCompositeDecl(Json json, Entity parent) 225 { 226 CompositeTypeDeclaration ret; 227 switch(json.kind.get!string){ 228 default: 229 logWarn("Invalid composite decl kind: %s", json.kind.get!string); 230 return new StructDeclaration(parent, json.name.get!string); 231 case "struct": 232 ret = new StructDeclaration(parent, json.name.get!string); 233 break; 234 case "union": 235 ret = new UnionDeclaration(parent, json.name.get!string); 236 break; 237 case "class": 238 auto clsdecl = new ClassDeclaration(parent, json.name.get!string); 239 if( clsdecl.qualifiedName != "object.Object" ) 240 clsdecl.baseClass = parseType(json.base, parent, "Object"); 241 foreach( intf; json.interfaces.opt!(Json[]) ) 242 clsdecl.derivedInterfaces ~= parseType(intf, parent); 243 ret = clsdecl; 244 break; 245 case "interface": 246 auto intfdecl = new InterfaceDeclaration(parent, json.name.get!string); 247 foreach( intf; json.interfaces.opt!(Json[]) ) 248 intfdecl.derivedInterfaces ~= parseType(intf, parent); 249 ret = intfdecl; 250 break; 251 } 252 253 insertIntoTypeMap(ret); 254 255 ret.members = parseDeclList(json.members, ret); 256 257 return ret; 258 } 259 260 auto parseVariableDecl(Json json, Entity parent) 261 { 262 auto ret = new VariableDeclaration(parent, json.name.get!string); 263 ret.type = parseType(json["type"], parent); 264 return ret; 265 } 266 267 auto parseTemplateDecl(Json json, Entity parent) 268 { 269 auto name = json.name.get!string().strip(); 270 auto cidx = name.countUntil('('); 271 assert(cidx > 0 && name.endsWith(')'), "Template name must be of the form name(args)"); 272 273 auto ret = new TemplateDeclaration(parent, name[0 .. cidx]); 274 ret.templateArgs = name[cidx+1 .. $-1].split(",").map!(a => new TemplateParameterDeclaration(ret, a.strip)).array; 275 ret.members = parseDeclList(json.members, ret); 276 return ret; 277 } 278 279 Type parseType(Json tp, Entity sc, string def_type = "void") 280 { 281 auto str = tp.opt!string; 282 if( str.length == 0 ) str = def_type; 283 auto tokens = tokenizeDSource(str); 284 285 logDebug("parse type '%s'", str); 286 try { 287 auto type = parseTypeDecl(tokens, sc); 288 type.text = str; 289 return type; 290 } catch( Exception e ){ 291 logError("Error parsing type '%s': %s", str, e.msg); 292 auto type = new Type; 293 type.text = str; 294 type.typeName = str; 295 type.kind = TypeKind.Primitive; 296 return type; 297 } 298 } 299 300 Value parseValue(string str) 301 { 302 auto ret = new Value; 303 //ret.type = ; 304 ret.valueString = str; 305 return ret; 306 } 307 308 Protection parseProtection(Json prot) 309 { 310 switch( prot.opt!string ){ 311 default: return Protection.Public; 312 case "package": return Protection.Package; 313 case "protected": return Protection.Protected; 314 case "private": return Protection.Private; 315 } 316 } 317 318 Declaration lookupDecl(string qualified_name, Entity sc) 319 { 320 while(sc){ 321 auto ent = cast(Declaration)sc.lookup(qualified_name); 322 if( ent ) return ent; 323 sc = sc.parent; 324 } 325 return null; 326 } 327 328 Type parseTypeDecl(ref string[] tokens, Entity sc) 329 { 330 static immutable global_attribute_keywords = ["abstract", "auto", "const", "deprecated", "enum", 331 "extern", "final", "immutable", "inout", "shared", "nothrow", "override", "pure", 332 "__gshared", "scope", "static", "synchronize"]; 333 334 static immutable parameter_attribute_keywords = ["auto", "const", "final", "immutable", "in", "inout", 335 "lazy", "out", "ref", "scope", "shared"]; 336 337 static immutable member_function_attribute_keywords = ["const", "immutable", "inout", "shared", "pure", "nothrow"]; 338 339 340 string[] attributes; 341 if( tokens.length > 0 && tokens[0] == "extern" ){ 342 enforce(tokens[1] == "("); 343 enforce(tokens[3] == ")"); 344 attributes ~= join(tokens[0 .. 4]); 345 tokens = tokens[4 .. $]; 346 } 347 348 immutable string[] attribute_keywords = global_attribute_keywords ~ parameter_attribute_keywords ~ member_function_attribute_keywords; 349 /*final switch( sc ){ 350 case DeclScope.Global: attribute_keywords = global_attribute_keywords; break; 351 case DeclScope.Parameter: attribute_keywords = parameter_attribute_keywords; break; 352 case DeclScope.Class: attribute_keywords = member_function_attribute_keywords; break; 353 }*/ 354 355 while( tokens.length > 0 ){ 356 if( tokens.front == "@" ){ 357 tokens.popFront(); 358 attributes ~= "@"~tokens.front; 359 tokens.popFront(); 360 } else if( attribute_keywords.countUntil(tokens[0]) >= 0 && tokens[1] != "(" ){ 361 attributes ~= tokens.front; 362 tokens.popFront(); 363 } else break; 364 } 365 366 auto ret = parseType(tokens, sc); 367 ret.attributes = attributes; 368 return ret; 369 } 370 371 Type parseType(ref string[] tokens, Entity sc) 372 { 373 auto basic_type = parseBasicType(tokens, sc); 374 375 while( tokens.length > 0 && (tokens[0] == "function" || tokens[0] == "delegate" || tokens[0] == "(") ){ 376 Type ret = new Type; 377 ret.kind = tokens.front == "(" || tokens.front == "function" ? TypeKind.Function : TypeKind.Delegate; 378 ret.returnType = basic_type; 379 if( tokens.front != "(" ) tokens.popFront(); 380 enforce(tokens.front == "("); 381 tokens.popFront(); 382 while(true){ 383 if( tokens.front == ")" ) break; 384 enforce(!tokens.empty); 385 ret.parameterTypes ~= parseTypeDecl(tokens, sc); 386 if( tokens.front != "," && tokens.front != ")" ){ 387 ret._parameterNames ~= tokens.front; 388 tokens.popFront(); 389 } else ret._parameterNames ~= null; 390 if( tokens.front == "..." ){ 391 ret._parameterNames[$-1] ~= tokens.front; 392 tokens.popFront(); 393 } 394 if( tokens.front == "=" ){ 395 tokens.popFront(); 396 string defval; 397 int ccount = 0; 398 while( !tokens.empty ){ 399 if( ccount == 0 && (tokens.front == "," || tokens.front == ")") ) 400 break; 401 if( tokens.front == "(" ) ccount++; 402 else if( tokens.front == ")" ) ccount--; 403 defval ~= tokens.front; 404 tokens.popFront(); 405 } 406 ret._parameterDefaultValues ~= parseValue(defval); 407 logDebug("got defval %s", defval); 408 } else ret._parameterDefaultValues ~= null; 409 if( tokens.front == ")" ) break; 410 enforce(tokens.front == ",", "Expecting ',', got "~tokens.front); 411 tokens.popFront(); 412 } 413 tokens.popFront(); 414 basic_type = ret; 415 } 416 417 return basic_type; 418 } 419 420 Type parseBasicType(ref string[] tokens, Entity sc) 421 { 422 Type type; 423 { 424 static immutable const_modifiers = ["const", "immutable", "shared", "inout"]; 425 string[] modifiers; 426 while( tokens.length > 2 ){ 427 if( tokens[1] == "(" && const_modifiers.countUntil(tokens[0]) >= 0 ){ 428 modifiers ~= tokens[0]; 429 tokens.popFrontN(2); 430 } else break; 431 } 432 433 434 if( modifiers.length > 0 ){ 435 type = parseBasicType(tokens, sc); 436 type.modifiers = modifiers; 437 foreach( i; 0 .. modifiers.length ){ 438 //enforce(tokens[i] == ")", "expected ')', got '"~tokens[i]~"'"); 439 if( tokens[i] == ")" ) // FIXME: this is a hack to make parsing(const(immutable(char)[][]) somehow "work" 440 tokens.popFront(); 441 } 442 //tokens.popFrontN(modifiers.length); 443 } else { 444 type = new Type; 445 type.kind = TypeKind.Primitive; 446 m_primTypes ~= tuple(type, sc); 447 448 size_t start = 0, end; 449 if( tokens[start] == "." ) start++; 450 for( end = start; end < tokens.length && isIdent(tokens[end]); ){ 451 end++; 452 if( end >= tokens.length || tokens[end] != "." ) 453 break; 454 end++; 455 } 456 457 size_t i = end; 458 459 string type_name, nested_name; 460 if( i == 0 && tokens[0] == "..." ){ 461 type_name = "..."; 462 nested_name = null; 463 } else if( i == 0 && tokens[0] == "(" ){ 464 type_name = "constructor"; 465 nested_name = null; 466 } else { 467 enforce(i > 0, "Expected identifier but got "~tokens.front); 468 type.typeName = join(tokens[start .. end]); 469 //type.typeDecl = cast(Declaration)sc.lookup(type.typeName); 470 tokens.popFrontN(i); 471 472 if( !tokens.empty && tokens.front == "!" ){ 473 tokens.popFront(); 474 if( tokens.front == "(" ){ 475 size_t j = 1; 476 int cc = 1; 477 while( cc > 0 ){ 478 assert(j < tokens.length); 479 if( tokens[j] == "(" ) cc++; 480 else if( tokens[j] == ")") cc--; 481 j++; 482 } 483 type.templateArgs = join(tokens[0 .. j]); 484 tokens.popFrontN(j); 485 logDebug("templargs: %s", type.templateArgs); 486 } else { 487 type.templateArgs = tokens[0]; 488 tokens.popFront(); 489 } 490 } 491 } 492 } 493 } 494 495 while( !tokens.empty ){ 496 if( tokens.front == "*" ){ 497 auto ptr = new Type; 498 ptr.kind = TypeKind.Pointer; 499 ptr.elementType = type; 500 type = ptr; 501 tokens.popFront(); 502 } else if( tokens.front == "[" ){ 503 tokens.popFront(); 504 if( tokens.front == "]" ){ 505 auto arr = new Type; 506 arr.kind = TypeKind.Array; 507 arr.elementType = type; 508 type = arr; 509 } else if( isDigit(tokens.front[0]) ){ 510 auto arr = new Type; 511 arr.kind = TypeKind.StaticArray; 512 arr.elementType = type; 513 arr.arrayLength = tokens.front;//to!int(tokens.front); 514 tokens.popFront(); 515 type = arr; 516 } else { 517 auto keytp = parseType(tokens, sc); 518 logDebug("GOT TYPE: %s", keytp.toString()); 519 auto aa = new Type; 520 aa.kind = TypeKind.AssociativeArray; 521 aa.elementType = type; 522 aa.keyType = keytp; 523 type = aa; 524 } 525 enforce(tokens.front == "]", "Expected ']', got '"~tokens.front~"'."); 526 tokens.popFront(); 527 } else break; 528 } 529 530 return type; 531 } 532 533 string[] tokenizeDSource(string dsource_) 534 { 535 static immutable dstring[] tokens = [ 536 "/", "/=", ".", "..", "...", "&", "&=", "&&", "|", "|=", "||", 537 "-", "-=", "--", "+", "+=", "++", "<", "<=", "<<", "<<=", 538 "<>", "<>=", ">", ">=", ">>=", ">>>=", ">>", ">>>", "!", "!=", 539 "!<>", "!<>=", "!<", "!<=", "!>", "!>=", "(", ")", "[", "]", 540 "{", "}", "?", ",", ";", ":", "$", "=", "==", "*", "*=", 541 "%", "%=", "^", "^=", "~", "~=", "@", "=>", "#" 542 ]; 543 static bool[dstring] token_map; 544 545 if( !token_map.length ){ 546 foreach( t; tokens ) 547 token_map[t] = true; 548 token_map.rehash; 549 } 550 551 dstring dsource = to!dstring(dsource_); 552 553 dstring[] ret; 554 outer: 555 while(true){ 556 dsource = stripLeft(dsource); 557 if( dsource.length == 0 ) break; 558 559 // special token? 560 foreach_reverse( i; 1 .. min(5, dsource.length+1) ) 561 if( dsource[0 .. i] in token_map ){ 562 ret ~= dsource[0 .. i]; 563 dsource.popFrontN(i); 564 continue outer; 565 } 566 567 // identifier? 568 if( dsource[0] == '_' || std.uni.isAlpha(dsource[0]) ){ 569 size_t i = 1; 570 while( i < dsource.length && (dsource[i] == '_' || std.uni.isAlpha(dsource[i]) || isDigit(dsource[i])) ) i++; 571 ret ~= dsource[0 .. i]; 572 dsource.popFrontN(i); 573 continue; 574 } 575 576 // character literal? 577 if( dsource[0] == '\'' ){ 578 size_t i = 1; 579 while( dsource[i] != '\'' ){ 580 if( dsource[i] == '\\' ) i++; 581 i++; 582 enforce(i < dsource.length); 583 } 584 ret ~= dsource[0 .. i+1]; 585 dsource.popFrontN(i+1); 586 continue; 587 } 588 589 // string? (incomplete!) 590 if( dsource[0] == '"' ){ 591 size_t i = 1; 592 while( dsource[i] != '"' ){ 593 if( dsource[i] == '\\' ) i++; 594 i++; 595 enforce(i < dsource.length); 596 } 597 ret ~= dsource[0 .. i+1]; 598 dsource.popFrontN(i+1); 599 continue; 600 } 601 602 // number? 603 if( isDigit(dsource[0]) || dsource[0] == '.' ){ 604 auto dscopy = dsource; 605 parse!double(dscopy); 606 ret ~= dsource[0 .. dsource.length-dscopy.length]; 607 dsource.popFrontN(dsource.length-dscopy.length); 608 if( dsource.startsWith("u") ) dsource.popFront(); 609 else if( dsource.startsWith("f") ) dsource.popFront(); 610 continue; 611 } 612 613 ret ~= dsource[0 .. 1]; 614 dsource.popFront(); 615 } 616 617 auto ret_ = new string[ret.length]; 618 foreach( i; 0 .. ret.length ) ret_[i] = to!string(ret[i]); 619 return ret_; 620 } 621 622 bool isDigit(dchar ch) 623 { 624 return ch >= '0' && ch <= '9'; 625 } 626 627 bool isIdent(string str) 628 { 629 if( str.length < 1 ) return false; 630 foreach( i, dchar ch; str ){ 631 if( ch == '_' || std.uni.isAlpha(ch) ) continue; 632 if( i > 0 && isDigit(ch) ) continue; 633 return false; 634 } 635 return true; 636 } 637 638 string fullStrip(string s) 639 { 640 string chars = " \t\r\n"; 641 while( s.length > 0 && chars.countUntil(s[0]) >= 0 ) s.popFront(); 642 while( s.length > 0 && chars.countUntil(s[$-1]) >= 0 ) s.popBack(); 643 return s; 644 } 645 646 void insertIntoTypeMap(Declaration decl) 647 { 648 string[] parts = split(decl.qualifiedName, "."); 649 foreach( i; 0 .. parts.length ){ 650 auto partial_name = join(parts[i .. $], "."); 651 m_typeMap[partial_name] = decl; 652 } 653 } 654 } 655