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 }