1 /**
2  * This module crawl the AST to resolve identifiers and process types.
3  */
4 module d.semantic.dmodule;
5 
6 import d.semantic.scheduler;
7 import d.semantic.semantic;
8 
9 import d.ast.declaration;
10 
11 import d.ir.symbol;
12 
13 import source.name;
14 
15 alias AstModule = d.ast.declaration.Module;
16 alias Module = d.ir.symbol.Module;
17 
18 alias PackageNames = Name[];
19 
20 struct ModuleVisitorData {
21 private:
22 	Module[string] cachedModules;
23 }
24 
25 struct ModuleVisitor {
26 private:
27 	SemanticPass pass;
28 	alias pass this;
29 	
30 	@property
31 	ref Module[string] cachedModules() {
32 		return pass.moduleVisitorData.cachedModules;
33 	}
34 	
35 public:
36 	this(SemanticPass pass) {
37 		this.pass = pass;
38 	}
39 	
40 	Module importModule(PackageNames packages) {
41 		import std.algorithm, std.range;
42 		auto name = packages.map!(p => p.toString(pass.context)).join(".");
43 		
44 		return cachedModules.get(name, {
45 			import std.algorithm, std.array, std.path;
46 			auto basename = packages
47 				.map!(p => p.toString(pass.context))
48 				.buildPath();
49 			
50 			auto filename = basename ~ ".d";
51 			auto dir = getIncludeDir(filename, includePaths);
52 			
53 			auto astm = parse(filename, dir);
54 			auto mod = modulize(astm);
55 			
56 			pass.scheduler.schedule(astm, mod);
57 			return cachedModules[name] = mod;
58 		}());
59 	}
60 	
61 	Module add(string filename) {
62 		import std.conv, std.path;
63 		filename = expandTilde(filename)
64 			.asAbsolutePath
65 			.asNormalizedPath
66 			.to!string();
67 		
68 		// Try to find the module in include path.
69 		string dir;
70 		foreach(path; includePaths) {
71 			if (path.length < dir.length) {
72 				continue;
73 			}
74 			
75 			import std.algorithm;
76 			if (filename.startsWith(path)) {
77 				dir = path;
78 			}
79 		}
80 		
81 		// XXX: this.parse, because dmd is insane and want to use std.conv :(
82 		auto astm = this.parse(relativePath(filename, dir), dir);
83 		auto mod = modulize(astm);
84 		cachedModules[getModuleName(mod)] = mod;
85 		
86 		scheduler.schedule(astm, mod);
87 		return mod;
88 	}
89 	
90 	AstModule parse(string filename, string directory) in {
91 		assert(filename[$ - 2 .. $] == ".d");
92 	} do {
93 		import source.location;
94 		auto base = context.registerFile(Location.init, filename, directory);
95 		
96 		import source.dlexer;
97 		auto l = lex(base, context);
98 		
99 		import d.parser.dmodule;
100 		auto m = l.parseModule();
101 		
102 		import std.algorithm, std.array, std.path;
103 		auto packages = filename[0 .. $ - 2]
104 			.pathSplitter()
105 			.map!(p => pass.context.getName(p))
106 			.array();
107 		
108 		auto name = packages[$ - 1];
109 		packages = packages[0 .. $ - 1];
110 		
111 		// If we have no module declaration, we infer it from the file.
112 		if (m.name == BuiltinName!"") {
113 			m.name = name;
114 			m.packages = packages;
115 		} else {
116 			// XXX: Do proper error checking. Consider doing fixup.
117 			assert(m.name == name, "Wrong module name");
118 			assert(m.packages == packages, "Wrong module package");
119 		}
120 		
121 		return m;
122 	}
123 	
124 	Module modulize(AstModule astm) {
125 		auto loc = astm.location;
126 		
127 		auto m = new Module(loc, astm.name, null);
128 		m.addSymbol(m);
129 		
130 		Package p;
131 		foreach(n; astm.packages) {
132 			p = new Package(loc, n, p);
133 		}
134 		
135 		m.parent = p;
136 		p = m;
137 		while (p.parent !is null) {
138 			p.parent.addSymbol(p);
139 			p = p.parent;
140 		}
141 		
142 		return m;
143 	}
144 	
145 	private auto getModuleName(Module m) {
146 		auto name = m.name.toString(context);
147 		if (m.parent) {
148 			auto dpackage = m.parent;
149 			while(dpackage) {
150 				name = dpackage.name.toString(context) ~ "." ~ name;
151 				dpackage = dpackage.parent;
152 			}
153 		}
154 		
155 		return name;
156 	}
157 }
158 
159 private:
160 string getIncludeDir(string filename, const string[] includePaths) {
161 	foreach(path; includePaths) {
162 		import std.path;
163 		auto fullpath = buildPath(path, filename);
164 		
165 		import std.file;
166 		if (exists(fullpath)) {
167 			return path;
168 		}
169 	}
170 	
171 	// XXX: handle properly ? Now it will fail down the road.
172 	return "";
173 }