1 /// Author: Aziz Köksal 2 /// License: GPL3 3 /// $(Maturity high) 4 module dil.semantic.Module; 5 6 import dil.ast.Node, 7 dil.ast.Declarations; 8 import dil.parser.Parser; 9 import dil.lexer.Lexer, 10 dil.lexer.IdTable; 11 import dil.semantic.Symbol, 12 dil.semantic.Symbols; 13 import dil.i18n.Messages; 14 import dil.Compilation, 15 dil.Location, 16 dil.Diagnostics, 17 dil.SourceText, 18 dil.String; 19 import util.Path; 20 import common; 21 22 import std.file; 23 24 /// Represents a semantic D module and a source file. 25 class Module : ModuleSymbol 26 { 27 SourceText sourceText; /// The source file of this module. 28 cstring moduleFQN; /// Fully qualified name of the module. E.g.: dil.ast.Node 29 cstring packageName; /// E.g.: dil.ast 30 cstring moduleName; /// E.g.: Node 31 size_t ID; /// A unique 1-based ID. Useful for graph traversing. 32 33 CompoundDecl root; /// The root of the parse tree. 34 ImportDecl[] imports; /// ImportDeclarations found in this file. 35 ModuleDecl moduleDecl; /// The optional ModuleDecl in this file. 36 Parser parser; /// The parser used to parse this file. 37 38 /// Indicates which passes have been run on this module. 39 /// 40 /// 0 = No pass.$(BR) 41 /// 1 = Semantic pass 1.$(BR) 42 /// 2 = Semantic pass 2. 43 uint semanticPass; 44 Module[] modules; /// The imported modules. 45 46 bool failedLoading; /// True if loading the source file failed. 47 48 CompilationContext cc; /// The compilation context. 49 50 /// Set when the Lexer should load the tokens from a file. 51 string dlxFilePath; 52 53 this() 54 { 55 super(); 56 } 57 58 /// Constructs a Module object. 59 /// Params: 60 /// filePath = File path to the source text; loaded in the constructor. 61 this(cstring filePath, CompilationContext cc) 62 { 63 this(); 64 this.cc = cc; 65 this.sourceText = new SourceText(filePath); 66 this.failedLoading = !this.sourceText.load(cc.diag); 67 } 68 69 /// Constructs from a ready-to-use SourceText instance. 70 this(SourceText src, CompilationContext cc) 71 { 72 this(); 73 this.cc = cc; 74 this.sourceText = src; 75 } 76 77 /// Returns the file path of the source text. 78 cstring filePath() 79 { 80 return sourceText.filePath; 81 } 82 83 /// Returns filePath and escapes '/' or '\' with '_'. 84 cstring filePathEsc() 85 { 86 return filePath().replace(dirSep, '_'); 87 } 88 89 /// Returns the file extension: "d" or "di". 90 cstring fileExtension() 91 { 92 auto i = String(filePath).findr('.'); 93 return i != -1 ? filePath[i..$] : ""; 94 } 95 96 /// Sets the parser to be used for parsing the source text. 97 void setParser(Parser parser) 98 { 99 this.parser = parser; 100 } 101 102 /// Parses the module. 103 void parse() 104 { 105 if (this.parser is null) 106 this.parser = new Parser(sourceText, cc.tables.lxtables, cc.diag); 107 108 if (this.dlxFilePath.length) 109 this.parser.lexer.fromDLXFile(cast(ubyte[])dlxFilePath.read()); 110 111 this.root = parser.start(); 112 this.imports = parser.imports; 113 114 // Set the fully qualified name of this module. 115 if (this.root.children.length) 116 { // moduleDecl will be null if first node isn't a ModuleDecl. 117 this.moduleDecl = this.root.children[0].Is!(ModuleDecl); 118 if (this.moduleDecl) 119 this.setFQN(moduleDecl.getFQN()); // E.g.: dil.ast.Node 120 } 121 122 auto idtable = cc.tables.idents; 123 124 if (!this.moduleFQN.length) 125 { // Take the base name of the file as the module name. 126 auto str = Path(filePath).name(); // E.g.: Node 127 if (!idtable.isValidUnreservedIdentifier(str)) 128 { 129 auto location = this.firstToken().getErrorLocation(filePath()); 130 auto msg = cc.diag.formatMsg(MID.InvalidModuleName, str); 131 cc.diag ~= new LexerError(location, msg); 132 str = idtable.genModuleID().str; 133 } 134 this.moduleFQN = this.moduleName = str; 135 } 136 assert(this.moduleFQN.length); 137 138 // Set the symbol name. 139 this.name = idtable.lookup(this.moduleName); 140 // Set the symbol node. 141 this.loc.n = this.root; 142 this.loc.t = this.moduleDecl ? this.moduleDecl.begin : this.firstToken(); 143 } 144 145 /// Returns the first token of the module's source text. 146 Token* firstToken() 147 { 148 return parser.lexer.firstToken(); 149 } 150 151 /// Returns true if there are errors in the source file. 152 bool hasErrors() 153 { 154 assert(parser && parser.lexer); 155 return parser.errors.length || parser.lexer.errors.length || failedLoading; 156 } 157 158 /// Returns a list of import paths. 159 /// E.g.: ["dil/ast/Node", "dil/semantic/Module"] 160 cstring[] getImportPaths() 161 { 162 cstring[] result; 163 foreach (import_; imports) 164 result ~= import_.getModuleFQNs(dirSep); 165 return result; 166 } 167 168 /// Returns the fully qualified name of this module. 169 /// E.g.: dil.ast.Node 170 override cstring getFQN() 171 { 172 return moduleFQN; 173 } 174 175 /// Sets the module's FQN. 176 void setFQN(cstring moduleFQN) 177 { 178 this.moduleFQN = moduleFQN; 179 auto i = String(moduleFQN).findr('.'); 180 if (i != -1) 181 this.packageName = moduleFQN[0..i]; 182 this.moduleName = moduleFQN[i+1..$]; 183 } 184 185 /// Returns the module's FQN with slashes instead of dots. 186 /// E.g.: dil.ast.Node -> dil/ast/Node 187 cstring getFQNPath() 188 { 189 return moduleFQN.replace('.', dirSep); 190 } 191 }