Mercurial > projects > dil
changeset 375:0bd21b746a04
- Added code to main() for recognizing options to the importgraph command.
- Added HelpImportGraph message.
- Added enum IGraphOption.
- Added class Vertex inheriting from Module. Changed struct Edge to a class.
- Modified a lot of code in cmd.ImportGraph; too many to list here.
- Made some minor changes to dil.Parser.
author | aziz |
---|---|
date | Sat, 08 Sep 2007 16:42:02 +0000 |
parents | 6d22f0b6a674 |
children | 5ebe80ce84f2 |
files | trunk/src/cmd/ImportGraph.d trunk/src/dil/Messages.d trunk/src/dil/Parser.d trunk/src/lang_de.d trunk/src/lang_en.d trunk/src/lang_fi.d trunk/src/lang_tr.d trunk/src/main.d |
diffstat | 8 files changed, 289 insertions(+), 71 deletions(-) [+] |
line wrap: on
line diff
--- a/trunk/src/cmd/ImportGraph.d Fri Sep 07 11:36:01 2007 +0000 +++ b/trunk/src/cmd/ImportGraph.d Sat Sep 08 16:42:02 2007 +0000 @@ -10,10 +10,24 @@ import dil.File; import dil.Module; import dil.Settings; -import std.stdio : writefln; +import std.stdio : writefln, writef; import std.path : getDirName, dirSep = sep; import std.file : exists; import std.string : replace; +import std.regexp; + +enum IGraphOption +{ + None, + IncludeUnlocatableModules = 1, + HighlightCyclicEdges = 1<<1, + HighlightCyclicVertices = 1<<2, + PrintDot = 1<<3, + PrintPaths = 1<<4, + PrintList = 1<<5, + GroupByPackageNames = 1<<6, + GroupByFullPackageName = 1<<7, +} string findModulePath(string moduleFQN, string[] importPaths) { @@ -27,121 +41,249 @@ return null; } -struct Edge +class Edge { - Module outgoing; - Module incoming; - static Edge opCall(Module o, Module i) + Vertex outgoing; + Vertex incoming; + bool isCyclic; + this(Vertex o, Vertex i) { - Edge e; - e.outgoing = o; - e.incoming = i; - return e; + this.outgoing = o; + this.incoming = i; } } -void execute(string fileName, string[] importPaths) +class Vertex : Module { + uint id; + Vertex[] incoming; + bool isCyclic; /// Whether this vertex is in a cyclic relationship with other vertices. + + this(string filePath) + { + super(filePath, true); + } + + Vertex[] outgoing() + { + return cast(Vertex[])super.modules; + } +} + +void execute(string filePath, string[] importPaths, string[] strRegexps, uint levels, IGraphOption options) +{ + // Init regular expressions. + RegExp[] regexps; + foreach (strRegexp; strRegexps) + regexps ~= new RegExp(strRegexp); + // Add directory of file and global directories to import paths. - auto fileDir = getDirName(fileName); + auto fileDir = getDirName(filePath); if (fileDir.length) importPaths ~= fileDir; importPaths ~= GlobalSettings.importPaths; - Module[string] loadedModules; - Module[] loadedModulesList; // Ordered list of loaded modules. + Vertex[string] loadedModules; + Vertex[] loadedModulesList; // Ordered list of loaded modules. Edge edges[]; - Module loadModule(string moduleFQNPath) + Vertex loadModule(string moduleFQNPath) { auto mod_ = moduleFQNPath in loadedModules; if (mod_ !is null) - return *mod_; -// writefln(moduleFQN); + return *mod_; // Return already loaded module. + + // Ignore module names matching regular expressions. + foreach (rx; regexps) + if (rx.test(replace(moduleFQNPath, dirSep, "."))) + return null; + auto modulePath = findModulePath(moduleFQNPath, importPaths); - Module mod; + Vertex mod; if (modulePath is null) { -// writefln("Warning: Module %s.d couldn't be found.", moduleFQNPath); - mod = new Module(null, true); - mod.setFQN(replace(moduleFQNPath, dirSep, ".")); - loadedModules[moduleFQNPath] = mod; - loadedModulesList ~= mod; + if (options & IGraphOption.IncludeUnlocatableModules) + { + mod = new Vertex(null); + mod.setFQN(replace(moduleFQNPath, dirSep, ".")); + loadedModules[moduleFQNPath] = mod; + loadedModulesList ~= mod; + mod.id = loadedModulesList.length -1; + } } else { - mod = new Module(modulePath, true); +// writefln(modulePath); + mod = new Vertex(modulePath); mod.parse(); loadedModules[moduleFQNPath] = mod; loadedModulesList ~= mod; + mod.id = loadedModulesList.length -1; auto moduleFQNs = mod.getImports(); foreach (moduleFQN_; moduleFQNs) { auto loaded_mod = loadModule(moduleFQN_); - edges ~= Edge(mod, loaded_mod); - mod.modules ~= loaded_mod; + if (loaded_mod !is null) + { + edges ~= new Edge(mod, loaded_mod); + mod.modules ~= loaded_mod; + loaded_mod.incoming ~= mod; + } } - return mod; } return mod; } - auto mod = new Module(fileName, true); + auto mod = new Vertex(filePath); mod.parse(); auto moduleFQNs = mod.getImports(); loadedModules[mod.getFQNPath()] = mod; loadedModulesList ~= mod; + mod.id = 0; // loadedModulesList.length -1 foreach (moduleFQN_; moduleFQNs) { auto loaded_mod = loadModule(moduleFQN_); - edges ~= Edge(mod, loaded_mod); - mod.modules ~= loaded_mod; + if (loaded_mod !is null) + { + edges ~= new Edge(mod, loaded_mod); + mod.modules ~= loaded_mod; + loaded_mod.incoming ~= mod; + } } + // Finished loading modules. + + + if (options & IGraphOption.PrintPaths) + printPaths(loadedModulesList, levels+1, ""); + else if (options & IGraphOption.PrintList) + printList(loadedModulesList, levels+1, ""); + else if (options & IGraphOption.PrintDot) + printDot(loadedModulesList, edges, options); +} + +void printPaths(Vertex[] vertices, uint level, char[] indent) +{ + if (!level) + return; + foreach (vertex; vertices) + { + writefln(indent, vertex.filePath); + if (vertex.outgoing.length) + printPaths(vertex.outgoing, level-1, indent~" "); + } +} - writefln("digraph module_dependencies\n{"); - foreach (edge; edges) +void printList(Vertex[] vertices, uint level, char[] indent) +{ + if (!level) + return; + foreach (vertex; vertices) { - writefln(` "%s" -> "%s"`, edge.outgoing.getFQN(), edge.incoming.getFQN()); + writefln(indent, vertex.getFQN()); + if (vertex.outgoing.length) + printList(vertex.outgoing, level-1, indent~" "); } +} + +void printDot(Vertex[] loadedModulesList, Edge[] edges, IGraphOption options) +{ + Vertex[][string] verticesByPckgName; + if (options & IGraphOption.GroupByFullPackageName) + foreach (module_; loadedModulesList) + verticesByPckgName[module_.packageName] ~= module_; + + if (options & (IGraphOption.HighlightCyclicVertices | + IGraphOption.HighlightCyclicEdges)) + analyzeGraph(loadedModulesList, edges.dup); + + writefln("Digraph ModuleDependencies\n{"); + + if (options & IGraphOption.HighlightCyclicVertices) + foreach (i, module_; loadedModulesList) + writefln(` n%d [label="%s"%s];`, i, module_.getFQN(), (module_.isCyclic ? ",style=filled,fillcolor=tomato" : "")); + else + foreach (i, module_; loadedModulesList) + writefln(` n%d [label="%s"];`, i, module_.getFQN()); + + foreach (edge; edges) + writefln(` n%d -> n%d%s;`, edge.outgoing.id, edge.incoming.id, (edge.isCyclic ? "[color=red]" : "")); + + if (options & IGraphOption.GroupByFullPackageName) + foreach (packageName, vertices; verticesByPckgName) + { + writef(` subgraph "cluster_%s" {`\n` label="%s";color=blue;`"\n ", packageName, packageName); + foreach (module_; vertices) + writef(`n%d;`, module_.id); + writefln("\n }"); + } + writefln("}"); } -Edge[] findCyclicEdges(Module[] modules, Edge[] edges) +void analyzeGraph(Vertex[] vertices, Edge[] edges) { - foreach (module_; modules) + void recursive(Vertex[] modules) { - uint outgoing, incoming; - foreach (edge; edges) + foreach (idx, vertex; vertices) { - if (edge.outgoing == module_) - outgoing++; - if (edge.incoming == module_) - incoming++; + uint outgoing, incoming; + foreach (j, edge; edges) + { + if (edge.outgoing is vertex) + outgoing++; + if (edge.incoming is vertex) + incoming++; + } + + if (outgoing == 0) + { + if (incoming != 0) + { + // Vertex is a sink. + alias outgoing i; // Reuse + alias incoming j; // Reuse + // Remove edges. + for (i=j=0; i < edges.length; i++) + if (edges[i].incoming !is vertex) + edges[j++] = edges[i]; + edges.length = j; + vertices = vertices[0..idx] ~ vertices[idx+1..$]; + return recursive(modules); + } + else + assert(0, "orphaned module: "~vertex.getFQN()~" (has no edges in graph)"); // orphaned vertex (module) in graph + } + else if (incoming == 0) + { + // Vertex is a source + alias outgoing i; // Reuse + alias incoming j; // Reuse + // Remove edges. + for (i=j=0; i < edges.length; i++) + if (edges[i].outgoing !is vertex) + edges[j++] = edges[i]; + edges.length = j; + vertices = vertices[0..idx] ~ vertices[idx+1..$]; + return recursive(modules); + } +// else +// { +// // source && sink +// } } - if (outgoing == 0) - { - if (incoming != 0) - { - // sink - } - else - assert(0); // orphaned vertex (module) in graph - } - else if (incoming == 0) - { - // source - } - else - { - // source && sink - } + // When reaching this point it means only cylic edges and vertices are left. + foreach (vertex; vertices) + vertex.isCyclic = true; + foreach (edge; edges) + if (edge) + edge.isCyclic = true; } - return null; + recursive(vertices); }
--- a/trunk/src/dil/Messages.d Fri Sep 07 11:36:01 2007 +0000 +++ b/trunk/src/dil/Messages.d Sat Sep 08 16:42:02 2007 +0000 @@ -66,6 +66,7 @@ // Help messages: HelpMain, HelpGenerate, + HelpImportGraph, } string GetMsg(MID mid)
--- a/trunk/src/dil/Parser.d Fri Sep 07 11:36:01 2007 +0000 +++ b/trunk/src/dil/Parser.d Sat Sep 08 16:42:02 2007 +0000 @@ -2593,8 +2593,8 @@ Expression parseAsmOrOrExpression() { + alias parseAsmAndAndExpression parseNext; auto begin = token; - alias parseAsmAndAndExpression parseNext; auto e = parseNext(); while (token.type == T.OrLogical) { @@ -2608,8 +2608,8 @@ Expression parseAsmAndAndExpression() { + alias parseAsmOrExpression parseNext; auto begin = token; - alias parseAsmOrExpression parseNext; auto e = parseNext(); while (token.type == T.AndLogical) { @@ -2623,8 +2623,8 @@ Expression parseAsmOrExpression() { + alias parseAsmXorExpression parseNext; auto begin = token; - alias parseAsmXorExpression parseNext; auto e = parseNext(); while (token.type == T.OrBinary) {
--- a/trunk/src/lang_de.d Fri Sep 07 11:36:01 2007 +0000 +++ b/trunk/src/lang_de.d Sat Sep 08 16:42:02 2007 +0000 @@ -70,6 +70,7 @@ erhalten. Kompiliert mit {3} v{4} am {5}.`, + `Generiere ein XML- oder HTML-Dokument aus einer D-Quelltextdatei. Verwendung: dil gen datei.d [Optionen] @@ -81,4 +82,6 @@ Beispiel: dil gen Parser.d --html --syntax > Parser.html`, + + ``, ]; \ No newline at end of file
--- a/trunk/src/lang_en.d Fri Sep 07 11:36:01 2007 +0000 +++ b/trunk/src/lang_en.d Sat Sep 08 16:42:02 2007 +0000 @@ -69,6 +69,7 @@ Type 'dil help <subcommand>' for more help on a particular subcommand. Compiled with {3} v{4} on {5}.`, + `Generate an XML or HTML document from a D source file. Usage: dil gen file.d [Options] @@ -80,4 +81,33 @@ Example: dil gen Parser.d --html --syntax > Parser.html`, + + `Parse a module and extract information from the resulting module dependency graph. +Usage: + dil igraph file.d Format [Options] + + The directory of file.d is implicitly added to the list of import paths. + +Format: + --dot : generate a dot document + -gbp : Group modules by package names + -gbf : Group modules by full package name + -hle : highlight cyclic edges in the graph + -hlv : highlight modules in cyclic relationship + + --paths : print a list of paths to the modules imported by file.d + -lN : print N levels. + + --list : print a list of the module names imported by file.d + -lN : print N levels. + +Options: + -Ipath : add 'path' to the list of import paths where modules are + looked for + -rREGEXP : exclude modules whose names match the regular expression + REGEXP + -i : include unlocatable modules + +Example: + dil igraph src/main.d`, ]; \ No newline at end of file
--- a/trunk/src/lang_fi.d Fri Sep 07 11:36:01 2007 +0000 +++ b/trunk/src/lang_fi.d Sat Sep 08 16:42:02 2007 +0000 @@ -82,4 +82,6 @@ Esimerkki: dil gen Parser.d --html --syntax > Parser.html`, + + ``, ]; \ No newline at end of file
--- a/trunk/src/lang_tr.d Fri Sep 07 11:36:01 2007 +0000 +++ b/trunk/src/lang_tr.d Sat Sep 08 16:42:02 2007 +0000 @@ -69,6 +69,7 @@ Belirli komut'a yardım edinmek için 'dil help <komut>' yazınız. Bu yazılım {3} v{4} ile {5} tarihinde derletilmiş.`, + `Bir D kaynak kodundan XML veya HTML dosyası oluştur. Kullanım: dil gen dosya.d [Seçenekler] @@ -80,4 +81,6 @@ Örnek: dil gen Parser.d --html --syntax > Parser.html`, + + ``, ]; \ No newline at end of file
--- a/trunk/src/main.d Fri Sep 07 11:36:01 2007 +0000 +++ b/trunk/src/main.d Sat Sep 08 16:42:02 2007 +0000 @@ -3,7 +3,6 @@ License: GPL3 +/ module main; -import std.stdio; import dil.Parser; import dil.Lexer; import dil.Token; @@ -14,6 +13,7 @@ import cmd.Generate; import cmd.Statistics; import cmd.ImportGraph; +import std.stdio, std.conv; void main(char[][] args) { @@ -47,19 +47,43 @@ cmd.Generate.execute(fileName, options); break; case "importgraph", "igraph": - string fileName; + string filePath; string[] includePaths; + string[] regexps; + uint levels; + IGraphOption options; foreach (arg; args[2..$]) { - if (arg.length >= 2 && arg[0..2] == "-I") - { - if (arg.length >= 3) - includePaths ~= args[2..$]; - } + if (strbeg(arg, "-I")) + includePaths ~= arg[2..$]; + else if(strbeg(arg, "-r")) + regexps ~= arg[2..$]; + else if(strbeg(arg, "-l")) + levels = toUint(arg[2..$]); else - fileName = arg; + switch (arg) + { + case "--dot": + options |= IGraphOption.PrintDot; break; + case "--paths": + options |= IGraphOption.PrintPaths; break; + case "--list": + options |= IGraphOption.PrintList; break; + case "-i": + options |= IGraphOption.IncludeUnlocatableModules; break; + case "-hle": + options |= IGraphOption.HighlightCyclicEdges; break; + case "-hlv": + options |= IGraphOption.HighlightCyclicVertices; break; + case "-gbp": + options |= IGraphOption.GroupByPackageNames; break; + case "-gbf": + options |= IGraphOption.GroupByFullPackageName; break; + default: + filePath = arg; + } } - cmd.ImportGraph.execute(fileName, includePaths); + cmd.ImportGraph.execute(filePath, includePaths, regexps, levels, options); break; case "stats", "statistics": cmd.Statistics.execute(args[2]); @@ -84,6 +108,16 @@ " importgraph (igraph)\n" " statistics (stats)\n"; +bool strbeg(char[] str, char[] begin) +{ + if (str.length >= begin.length) + { + if (str[0 .. begin.length] == begin) + return true; + } + return false; +} + char[] helpMain() { return format(MID.HelpMain, VERSION, COMMANDS, COMPILED_WITH, COMPILED_VERSION, COMPILED_DATE); @@ -97,6 +131,9 @@ case "gen", "generate": msg = GetMsg(MID.HelpGenerate); break; + case "importgraph", "igraph": + msg = GetMsg(MID.HelpImportGraph); + break; default: msg = helpMain(); } @@ -114,7 +151,7 @@ foreach(decl; decls) { assert(decl !is null); - writefln(indent, decl.classinfo.name, ": begin=%s end=%s", decl.begin ? decl.begin.srcText : "\33[31mnull\33[0m", decl.end ? decl.end.srcText : "\33[31mnull\33[0m"); +// writefln(indent, decl.classinfo.name, ": begin=%s end=%s", decl.begin ? decl.begin.srcText : "\33[31mnull\33[0m", decl.end ? decl.end.srcText : "\33[31mnull\33[0m"); print(decl.children, indent ~ " "); } }