1 /** 2 Directly parses D source code using libdparse. 3 4 Copyright: © 2014 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.dparse; 9 10 import ddox.ddox; 11 import ddox.entities; 12 import dparse = dparse.parser; 13 import dlex = dparse.lexer; 14 import dformat = dparse.formatter; 15 16 import std.algorithm; 17 import std.conv; 18 import std.exception; 19 import std.range; 20 import std.stdio; 21 import std.string; 22 import std.typecons; 23 import core.demangle; 24 import vibe.core.log; 25 import vibe.data.json; 26 27 // random dparser notes: 28 // Declaration.declarations[] ? 29 // Strings should be const(char)[] instead of string if transient 30 // how to get token name by token 31 // convenience functions for Formatter and unDecorateComment 32 // ambiguous representation of mutually exclusive values (maybe use subclassing? or enums for disambiguation?) 33 // InterfaceDecl.baseClassList <-> baseInterfaceList 34 // start/end line/column values for every AST node 35 // doc comments for alias this 36 // Declaration.attributeDeclaration vs. .attributes and .declarations? 37 // AliasDeclaration direct fields vs. initializers? 38 // Alias to non-type declarations is a type? 39 40 41 Package parseD(string[] d_files, Package root = null) 42 { 43 if (!root) root = new Package(null, null); 44 DParser p; 45 foreach (file; d_files) 46 p.parseModule(file, root); 47 p.resolveTypes(root); 48 return root; 49 } 50 51 52 private struct DParser 53 { 54 private Tuple!(Type, Entity)[] m_primTypes; 55 private Declaration[string] m_typeMap; 56 57 void resolveTypes(Package root) 58 { 59 bool isTypeDecl(Declaration a) 60 { 61 switch(a.kind){ 62 default: return false; 63 case DeclarationKind.Struct: 64 case DeclarationKind.Union: 65 case DeclarationKind.Class: 66 case DeclarationKind.Interface: 67 case DeclarationKind.Enum: 68 return true; 69 case DeclarationKind.Alias: 70 return (cast(AliasDeclaration)a).targetType !is null; 71 case DeclarationKind.TemplateParameter: 72 return true; 73 case DeclarationKind.Template: 74 // support eponymous template types 75 auto td = cast(TemplateDeclaration)a; 76 // be optimistic for templates without content that they are in fact types 77 if (!td.members.length) return true; 78 // otherwise require an actual eponymous type member 79 auto mi = td.members.countUntil!(m => m.name == a.name); 80 return mi >= 0 && isTypeDecl(td.members[mi]); 81 } 82 } 83 84 foreach (t; m_primTypes) { 85 auto decl = t[1].lookup!Declaration(t[0].typeName); 86 if (!decl || !isTypeDecl(decl)) { 87 auto pd = t[0].typeName in m_typeMap; 88 if (pd) decl = *pd; 89 } 90 if (decl && isTypeDecl(decl)) 91 t[0].typeDecl = decl; 92 } 93 94 // fixup class bases 95 root.visit!ClassDeclaration((decl){ 96 if (decl.baseClass && decl.baseClass.typeDecl && !cast(ClassDeclaration)decl.baseClass.typeDecl) 97 decl.baseClass.typeDecl = null; 98 99 foreach (ref i; decl.derivedInterfaces) { 100 if (i.typeDecl && !decl.baseClass && cast(ClassDeclaration)i.typeDecl) { 101 decl.baseClass = i; 102 i = null; 103 } else if (i.typeDecl && !cast(InterfaceDeclaration)i.typeDecl) { 104 i.typeDecl = null; 105 } 106 } 107 108 decl.derivedInterfaces = decl.derivedInterfaces.filter!(i => i !is null).array; 109 110 assert(decl); 111 }); 112 113 // fixup interface bases 114 root.visit!InterfaceDeclaration((decl){ 115 foreach( i; decl.derivedInterfaces ) 116 if( i.typeDecl && !cast(InterfaceDeclaration)i.typeDecl ) 117 i.typeDecl = null; 118 assert(decl); 119 }); 120 } 121 122 void parseModule(string filename, Package root_package) 123 { 124 import std.file; 125 126 dlex.LexerConfig config; 127 config.fileName = filename; 128 config.stringBehavior = dlex.StringBehavior.source; 129 dlex.StringCache cache = dlex.StringCache(1024 * 4); 130 auto tokens = dlex.getTokensForParser(cast(ubyte[])std.file.read(filename), config, &cache).array; 131 auto dmod = dparse.parseModule(tokens, filename); 132 133 Module mod; 134 if (!dmod.moduleDeclaration) { 135 logError("No module declaration in module %s - ignoring", filename); 136 return; 137 } 138 139 auto path = dmod.moduleDeclaration.moduleName.identifiers.map!(a => a.text.idup).array; 140 logInfo("MOD %s", path); 141 Package p = root_package; 142 foreach (i, pe; path) { 143 if (i+1 < path.length) p = p.getOrAddPackage(pe); 144 else mod = p.createModule(pe); 145 } 146 147 mod.file = filename; 148 mod.docGroup = new DocGroup(mod, dmod.moduleDeclaration.comment.undecorateComment()); 149 mod.members = parseDeclList(dmod.declarations, mod); 150 } 151 152 Declaration[] parseDeclList(const(dparse.Declaration)[] decls, Entity parent) 153 { 154 DocGroup lastdoc; 155 Declaration[] ret; 156 foreach (mem; decls) { 157 foreach (decl; parseDecl(mem, parent)) { 158 auto doc = decl.docGroup; 159 if (lastdoc && (doc.text == lastdoc.text && doc.text.length || doc.comment.isDitto)) { 160 lastdoc.members ~= decl; 161 decl.docGroup = lastdoc; 162 } else if (doc.comment.isPrivate) { 163 decl.protection = Protection.Private; 164 lastdoc = null; 165 } else lastdoc = decl.docGroup; 166 ret ~= decl; 167 } 168 } 169 return ret; 170 } 171 172 Declaration[] parseDecl(in dparse.Declaration decl, Entity parent, const(dparse.Attribute)[] additional_attribs = null) 173 { 174 if (auto ad = decl.attributeDeclaration) { 175 additional_attribs ~= decl.attributes; 176 additional_attribs ~= ad.attribute; 177 return decl.declarations.map!(d => parseDecl(d, parent, additional_attribs)).join(); 178 } 179 180 if (decl.declarations.length) { 181 additional_attribs ~= decl.attributes; 182 return decl.declarations.map!(d => parseDecl(d, parent, additional_attribs)).join(); 183 } 184 185 Declaration[] ret; 186 string comment; 187 size_t line; 188 if (auto fd = decl.functionDeclaration) { 189 comment = fd.comment.undecorateComment(); 190 line = fd.name.line; 191 192 auto fdr = new FunctionDeclaration(parent, fd.name.text.idup); 193 fdr.returnType = parseType(fd.returnType, parent); 194 fdr.parameters = parseParameters(fd.parameters, fdr); 195 fdr.type = new Type; 196 fdr.type.kind = TypeKind.Function; 197 //fdr.type.attributes = ...; // TODO! 198 //fdr.type.modifiers = ...; // TODO! 199 fdr.type.returnType = fdr.returnType; 200 fdr.type.parameterTypes = fdr.parameters.map!(p => p.type).array; 201 addAttributes(fdr, fd.attributes); 202 addTemplateInfo(fdr, fd); 203 204 ret ~= fdr; 205 } else if (auto vd = decl.variableDeclaration) { 206 comment = vd.comment.undecorateComment(); 207 line = vd.declarators[0].name.line; 208 auto tp = parseType(vd.type, parent); 209 foreach (d; vd.declarators) { 210 auto v = new VariableDeclaration(parent, d.name.text.idup); 211 v.type = tp; 212 if (d.initializer) v.initializer = new Value(tp, formatNode(d.initializer)); 213 ret ~= v; 214 } 215 } else if (auto at = decl.aliasThisDeclaration) { 216 // TODO comment? 217 line = at.identifier.line; 218 auto adr = new AliasDeclaration(parent, "this"); 219 adr.targetString = at.identifier.text.idup; 220 ret ~= adr; 221 } else if (auto sd = decl.structDeclaration) { 222 comment = sd.comment.undecorateComment(); 223 line = sd.name.line; 224 auto sdr = new StructDeclaration(parent, sd.name.text.idup); 225 sdr.members = parseDeclList(sd.structBody.declarations, sdr); 226 addTemplateInfo(sdr, sd); 227 ret ~= sdr; 228 insertIntoTypeMap(sdr); 229 } else if (auto cd = decl.classDeclaration) { 230 comment = cd.comment.undecorateComment(); 231 line = cd.name.line; 232 auto cdr = new ClassDeclaration(parent, cd.name.text.idup); 233 if (cd.baseClassList) foreach (bc; cd.baseClassList.items) { 234 auto t = new Type; 235 t.kind = TypeKind.Primitive; 236 t.typeName = formatNode(bc); 237 cdr.derivedInterfaces ~= t; 238 m_primTypes ~= tuple(t, parent); 239 } 240 cdr.members = parseDeclList(cd.structBody.declarations, cdr); 241 addTemplateInfo(cdr, cd); 242 ret ~= cdr; 243 insertIntoTypeMap(cdr); 244 } else if (auto id = decl.interfaceDeclaration) { 245 comment = id.comment.undecorateComment(); 246 line = id.name.line; 247 auto idr = new InterfaceDeclaration(parent, id.name.text.idup); 248 if (id.baseClassList) foreach (bc; id.baseClassList.items) { 249 auto t = new Type; 250 t.kind = TypeKind.Primitive; 251 t.typeName = formatNode(bc); 252 idr.derivedInterfaces ~= t; 253 m_primTypes ~= tuple(t, parent); 254 } 255 idr.members = parseDeclList(id.structBody.declarations, idr); 256 addTemplateInfo(idr, id); 257 ret ~= idr; 258 insertIntoTypeMap(idr); 259 } else if (auto ud = decl.unionDeclaration) { 260 comment = ud.comment.undecorateComment(); 261 line = ud.name.line; 262 auto udr = new UnionDeclaration(parent, ud.name.text.idup); 263 udr.members = parseDeclList(ud.structBody.declarations, udr); 264 addTemplateInfo(udr, ud); 265 ret ~= udr; 266 insertIntoTypeMap(udr); 267 } else if (auto ed = decl.enumDeclaration) { 268 logInfo("TODO: enum %s.%s", parent.qualifiedName, ed.name.text); 269 // TODO 270 return null; 271 } else if (auto ad = decl.aliasDeclaration) { 272 comment = ad.comment.undecorateComment(); 273 assert(ad.initializers.length); 274 line = ad.initializers[0].name.line; 275 foreach (ai; ad.initializers) { 276 auto adr = new AliasDeclaration(parent, ai.name.text.idup); 277 adr.targetType = parseType(ai.type, parent); 278 adr.targetString = formatNode(ai.type); 279 ret ~= adr; 280 } 281 } else if (auto td = decl.templateDeclaration) { 282 logInfo("TODO: template %s.%s", parent.qualifiedName, td.name.text); 283 // TODO 284 return null; 285 } else if (auto cd = decl.constructor) { 286 comment = cd.comment.undecorateComment(); 287 line = cd.line; 288 289 auto cdr = new FunctionDeclaration(parent, "this"); 290 cdr.parameters = parseParameters(cd.parameters, cdr); 291 cdr.type = new Type; 292 cdr.type.kind = TypeKind.Function; 293 cdr.type.parameterTypes = cdr.parameters.map!(p => p.type).array; 294 //addAttributes(cdr, cd.memberFunctionAttributes); // TODO! 295 addTemplateInfo(cdr, cd); 296 ret ~= cdr; 297 } else if (auto dd = decl.destructor) { 298 // destructors don't get documented for now 299 return null; 300 } else if (auto scd = decl.staticConstructor) { 301 logInfo("TODO: %s.static this()", parent.qualifiedName); 302 // TODO 303 return null; 304 } else if (auto sdd = decl.staticDestructor) { 305 logInfo("TODO: %s.static ~this()", parent.qualifiedName); 306 // TODO 307 return null; 308 } else if (auto pbd = decl.postblit) { 309 // postblit doesn't get documented for now 310 return null; 311 } else if (auto id = decl.importDeclaration) { 312 // TODO: use for type resolution 313 return null; 314 } else if (auto id = decl.unittest_) { 315 // TODO: use for unit test examples! 316 logInfo("TODO: %s.unittest", parent.qualifiedName); 317 return null; 318 } else { 319 logInfo("Unknown declaration in %s: %s", parent.qualifiedName, formatNode(decl)); 320 return null; 321 } 322 323 if (!ret) return null; 324 325 foreach (d; ret) { 326 addAttributes(d, additional_attribs); 327 addAttributes(d, decl.attributes); 328 329 d.docGroup = new DocGroup(d, comment); 330 d.line = line.to!int; 331 } 332 333 return ret; 334 } 335 336 void addAttributes(Declaration decl, const(dparse.Attribute)[] attrs) 337 { 338 return addAttributes(decl, attrs.map!(att => formatNode(att)).array); 339 } 340 341 void addAttributes(Declaration decl, string[] attrs) 342 { 343 foreach (as; attrs) { 344 switch (as) { 345 default: 346 if (!decl.attributes.canFind(as)) 347 decl.attributes ~= as; 348 break; 349 case "private": decl.protection = Protection.Private; break; 350 case "package": decl.protection = Protection.Package; break; 351 case "protected": decl.protection = Protection.Protected; break; 352 case "public": decl.protection = Protection.Public; break; 353 } 354 } 355 } 356 357 VariableDeclaration[] parseParameters(in dparse.Parameters dparams, FunctionDeclaration parent) 358 { 359 VariableDeclaration[] ret; 360 foreach (p; dparams.parameters) { 361 ret ~= parseParameter(p, parent); 362 if (p.vararg) ret[$-1].name ~= "..."; 363 } 364 if (dparams.hasVarargs) 365 ret ~= new VariableDeclaration(parent, "..."); 366 return ret; 367 } 368 369 VariableDeclaration parseParameter(in dparse.Parameter dparam, FunctionDeclaration parent) 370 { 371 auto ret = new VariableDeclaration(parent, dparam.name.text.idup); 372 ret.type = parseType(dparam.type, parent); 373 if (dparam.default_) { 374 ret.initializer = new Value; 375 ret.initializer.type = ret.type; 376 ret.initializer.valueString = formatNode(dparam.default_); 377 } 378 addAttributes(ret, dparam.parameterAttributes.map!(a => dlex.str(a)).array); 379 return ret; 380 } 381 382 Type parseType(in dparse.Type type, Entity scope_) 383 { 384 auto ret = parseType(type.type2, scope_); 385 foreach (tc; type.typeConstructors) 386 ret.modifiers ~= dlex.str(tc); 387 388 foreach (sf; type.typeSuffixes) { 389 if (sf.delegateOrFunction.text) { 390 if (sf.delegateOrFunction.text == "function") 391 ret = Type.makeFunction(ret, parseParameters(sf.parameters, null).map!(p => p.type).array); 392 else ret = Type.makeDelegate(ret, parseParameters(sf.parameters, null).map!(p => p.type).array); 393 } 394 else if (sf.star.type != dlex.tok!"") ret = Type.makePointer(ret); 395 else if (sf.array) { 396 if (sf.type) ret = ret.makeAssociativeArray(ret, parseType(sf.type, scope_)); 397 else if (sf.low) ret = ret.makeStaticArray(ret, formatNode(sf.low)); 398 else ret = Type.makeArray(ret); 399 } 400 } 401 402 ret.text = formatNode(type); 403 return ret; 404 } 405 406 Type parseType(in dparse.Type2 type, Entity scope_) 407 { 408 auto ret = new Type; 409 if (type.builtinType) { 410 ret.kind = TypeKind.Primitive; 411 ret.typeName = dlex.str(type.builtinType); 412 } else if (type.symbol) { 413 ret.kind = TypeKind.Primitive; 414 ret.typeName = formatNode(type.symbol); 415 m_primTypes ~= tuple(ret, scope_); 416 } else if (auto te = type.typeofExpression) { 417 ret.kind = TypeKind.Primitive; 418 ret.typeName = formatNode(te.expression); 419 // te.return_? 420 } else if (auto itc = type.identifierOrTemplateChain) { 421 ret.kind = TypeKind.Primitive; 422 ret.typeName = itc.identifiersOrTemplateInstances 423 .map!(it => it.templateInstance ? formatNode(it.templateInstance) : it.identifier.text.idup) 424 .join("."); 425 m_primTypes ~= tuple(ret, scope_); 426 } else if (auto tc = type.typeConstructor) { 427 ret = parseType(type.type, scope_); 428 ret.modifiers = dlex.str(tc) ~ ret.modifiers; 429 } else if (auto tp = type.type) { 430 return parseType(tp, scope_); 431 } else { 432 ret.kind = TypeKind.Primitive; 433 ret.typeName = "(invalid)"; 434 } 435 return ret; 436 } 437 438 void insertIntoTypeMap(Declaration decl) 439 { 440 string[] parts = split(decl.qualifiedName, "."); 441 foreach( i; 0 .. parts.length ){ 442 auto partial_name = join(parts[i .. $], "."); 443 m_typeMap[partial_name] = decl; 444 } 445 } 446 } 447 448 449 private string undecorateComment(string str) 450 { 451 if (!str.length) return ""; 452 453 auto app = appender!string(); 454 dlex.unDecorateComment(str, app); 455 return app.data; 456 } 457 458 459 private string formatNode(T)(const T t) 460 { 461 import std.array; 462 auto writer = appender!string(); 463 auto formatter = new dformat.Formatter!(typeof(writer))(writer); 464 formatter.format(t); 465 return writer.data; 466 } 467 468 private void addTemplateInfo(T)(Declaration decl, T ddecl) 469 { 470 if (ddecl.templateParameters) { 471 decl.isTemplate = true; 472 if (auto tpl = ddecl.templateParameters.templateParameterList) 473 decl.templateArgs = tpl.items.map!(tp => new TemplateParameterDeclaration(decl, formatNode(tp))).array; 474 if (ddecl.constraint) 475 decl.templateConstraint = formatNode(ddecl.constraint.expression); 476 } 477 }