1 // Copyright Brian Schott (Hackerpilot) 2012. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt or copy at 4 // http://www.boost.org/LICENSE_1_0.txt) 5 6 module dscanner.imports; 7 8 import dparse.ast; 9 import dparse.lexer; 10 import dparse.parser; 11 import dparse.rollback_allocator; 12 import std.stdio; 13 import std.container.rbtree; 14 import std.functional : toDelegate; 15 import dscanner.utils; 16 17 /** 18 * AST visitor that collects modules imported to an R-B tree. 19 */ 20 class ImportPrinter : ASTVisitor 21 { 22 this() 23 { 24 imports = new RedBlackTree!string; 25 } 26 27 override void visit(const SingleImport singleImport) 28 { 29 ignore = false; 30 singleImport.accept(this); 31 ignore = true; 32 } 33 34 override void visit(const IdentifierChain identifierChain) 35 { 36 if (ignore) 37 return; 38 bool first = true; 39 string s; 40 foreach (ident; identifierChain.identifiers) 41 { 42 if (!first) 43 s ~= "."; 44 s ~= ident.text; 45 first = false; 46 } 47 imports.insert(s); 48 } 49 50 alias visit = ASTVisitor.visit; 51 52 /// Collected imports 53 RedBlackTree!string imports; 54 55 private: 56 bool ignore = true; 57 } 58 59 private void visitFile(bool usingStdin, string fileName, RedBlackTree!string importedModules, StringCache* cache) 60 { 61 RollbackAllocator rba; 62 LexerConfig config; 63 config.fileName = fileName; 64 config.stringBehavior = StringBehavior.source; 65 auto visitor = new ImportPrinter; 66 auto tokens = getTokensForParser(usingStdin ? readStdin() : readFile(fileName), config, cache); 67 auto mod = parseModule(tokens, fileName, &rba, toDelegate(&doNothing)); 68 visitor.visit(mod); 69 importedModules.insert(visitor.imports[]); 70 } 71 72 private void doNothing(string, size_t, size_t, string, bool) 73 { 74 } 75 76 void printImports(bool usingStdin, string[] args, string[] importPaths, StringCache* cache, bool recursive) 77 { 78 string[] fileNames = usingStdin ? ["stdin"] : expandArgs(args); 79 import std.path : buildPath, dirSeparator; 80 import std.file : isFile, exists; 81 import std.array : replace, empty; 82 import std.range : chain, only; 83 84 auto resolvedModules = new RedBlackTree!(string); 85 auto resolvedLocations = new RedBlackTree!(string); 86 auto importedFiles = new RedBlackTree!(string); 87 foreach (name; fileNames) 88 visitFile(usingStdin, name, importedFiles, cache); 89 if (importPaths.empty) 90 { 91 foreach (item; importedFiles[]) 92 writeln(item); 93 return; 94 } 95 while (!importedFiles.empty) 96 { 97 auto newlyDiscovered = new RedBlackTree!(string); 98 itemLoop: foreach (item; importedFiles[]) 99 { 100 foreach (path; importPaths) 101 { 102 auto d = buildPath(path, item.replace(".", dirSeparator) ~ ".d"); 103 auto di = buildPath(path, item.replace(".", dirSeparator) ~ ".di"); 104 auto p = buildPath(path, item.replace(".", dirSeparator), "package.d"); 105 auto pi = buildPath(path, item.replace(".", dirSeparator), "package.di"); 106 foreach (alt; [d, di, p, pi]) 107 { 108 if (exists(alt) && isFile(alt)) 109 { 110 resolvedModules.insert(item); 111 resolvedLocations.insert(alt); 112 if (recursive) 113 visitFile(false, alt, newlyDiscovered, cache); 114 continue itemLoop; 115 } 116 } 117 } 118 writeln("Could not resolve location of ", item); 119 } 120 foreach (item; importedFiles[]) 121 newlyDiscovered.removeKey(item); 122 foreach (resolved; resolvedModules[]) 123 newlyDiscovered.removeKey(resolved); 124 importedFiles = newlyDiscovered; 125 } 126 foreach (resolved; resolvedLocations[]) 127 writeln(resolved); 128 }