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