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 }