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