1 /// Author: Aziz Köksal
2 /// License: GPL3
3 /// $(Maturity low)
4 module cmd.Compile;
5 
6 import cmd.Command;
7 import dil.ast.Declarations;
8 import dil.lexer.Token;
9 import dil.semantic.Module,
10        dil.semantic.Package,
11        dil.semantic.Pass1,
12        dil.semantic.Pass2,
13        dil.semantic.Passes,
14        dil.semantic.Symbol,
15        dil.semantic.Symbols;
16 import dil.doc.Doc;
17 import dil.i18n.Messages;
18 import dil.Compilation,
19        dil.Diagnostics,
20        dil.ModuleManager,
21        dil.String;
22 import util.Path;
23 import common;
24 
25 /// The compile command.
26 class CompileCommand : Command
27 {
28   cstring[] filePaths; /// Explicitly specified modules (on the command line.)
29   bool printSymbolTree; /// Whether to print the symbol tree.
30   bool printModuleTree; /// Whether to print the module tree.
31   bool m32; /// Emit 32bit code.
32   bool m64; /// Emit 64bit code.
33 
34   cstring binOutput; /// Output destination.
35 
36   ModuleManager moduleMan;
37   SemanticPass1[] passes1;
38 
39   CompilationContext context;
40   Diagnostics diag;
41 
42   /// Executes the compile command.
43   override void run()
44   {
45     // TODO: import object.d
46     moduleMan = new ModuleManager(context);
47     foreach (filePath; filePaths)
48       moduleMan.loadModuleFile(filePath);
49 
50     foreach (modul; moduleMan.loadedModules)
51     {
52       runPass1(modul);
53       if (printSymbolTree)
54         printSymbolTable(modul, "");
55     }
56 
57     // foreach (modul; moduleMan.loadedModules)
58     // {
59     //   auto pass2 = new SemanticPass2(modul);
60     //   pass2.run();
61     // }
62 
63     if (printModuleTree)
64     {
65       moduleMan.sortPackageTree();
66       printMTree2(moduleMan.rootPackage, "");
67     }
68     // foreach (mod; moduleMan.orderedModules)
69     //   Stdout(mod.moduleFQN, mod.imports.length).newline;
70   }
71 
72   /// Prints the package/module tree including the root.
73   void printMTree(Package pckg, cstring indent)
74   {
75     Stdout(indent)(pckg.pckgName)("/").newline; // PackageName/
76     foreach (p; pckg.packages)
77       printMTree(p, indent ~ "  "); // Print the sub-packages.
78     foreach (m; pckg.modules) // Print the modules.
79       Stdout(indent ~ "  ")(m.moduleName)(".")(m.fileExtension()).newline;
80   }
81 
82   /// Prints the package/module tree excluding the root.
83   void printMTree2(Package pckg, cstring indent)
84   {
85     foreach (p; pckg.packages)
86     {
87       Stdout(indent)(p.pckgName)("/").newline; // PackageName/
88       printMTree2(p, indent ~ "  "); // Print the sub-packages.
89     }
90     foreach (m; pckg.modules) // Print the modules.
91       Stdout(indent)(m.moduleName)(".")(m.fileExtension()).newline;
92   }
93 
94   /// Runs the first pass on modul.
95   void runPass1(Module modul)
96   {
97     if (modul.hasErrors || modul.semanticPass != 0)
98       return;
99     auto pass1 = new SemanticPass1(modul, context);
100     pass1.importModule = &importModule;
101     pass1.run();
102     passes1 ~= pass1;
103   }
104 
105   /// Imports a module and runs the first pass on it.
106   Module importModule(cstring moduleFQNPath)
107   {
108     auto modul = moduleMan.loadModule(moduleFQNPath);
109     modul && runPass1(modul);
110     return modul;
111   }
112 
113   /// Prints all symbols recursively (for debugging.)
114   static void printSymbolTable(ScopeSymbol scopeSym, cstring indent)
115   {
116     foreach (member; scopeSym.members)
117     {
118       auto tokens = DDocUtils.getDocTokens(member.loc.n);
119       char[] docText;
120       foreach (token; tokens)
121         docText ~= token.text;
122       Stdout(indent).formatln("Id:{}, Symbol:{}, DocText:{}",
123                               member.name.str, typeid(member).name,
124                               docText);
125       if (auto s = cast(ScopeSymbol)member)
126         printSymbolTable(s, indent ~ "→ ");
127     }
128   }
129 }
130 
131 
132 /// The compile command.
133 /// NOTE: The plan is to replace CompileCommand.
134 class CompileCommand2 : Command
135 {
136   /// For finding and loading modules.
137   ModuleManager mm;
138   /// Context information.
139   CompilationContext cc;
140   /// Explicitly specified modules (on the command line.)
141   cstring[] filePaths;
142   /// Whether to print the symbol tree.
143   bool printSymbolTree;
144   /// Whether to print the module tree.
145   bool printModuleTree;
146 
147   // Format strings for logging.
148   auto LogPass1  = "pass1:  {}";
149   auto LogPass2  = "pass2:  {}";
150   auto LogDeps   = "deps:   {}";
151   auto LogLoad   = "load:   {}";
152   auto LogImport = "import: {} ({})";
153   auto LogDiags  = "diagnostics:";
154 
155   /// Runs semantic pass 1 on a module. Also imports its dependencies.
156   void runPass1(Module modul)
157   {
158     if (modul.hasErrors || modul.semanticPass != 0)
159       return;
160 
161     lzy(log(LogPass1, modul.getFQN()));
162 
163     auto pass1 = new FirstSemanticPass(modul);
164     pass1.run();
165 
166     lzy({if (pass1.imports.length) log(LogDeps, modul.getFQN());}());
167 
168     // Load the module's imported modules.
169     foreach (d; pass1.imports)
170       importModuleByDecl(d, modul);
171       // TODO: modul.modules ~= importedModule;
172   }
173 
174   /// Runs semantic pass 2 on a module.
175   void runPass2(Module modul)
176   {
177     if (modul.hasErrors || modul.semanticPass != 1)
178       return;
179 
180     lzy(log(LogPass2, modul.getFQN()));
181 
182     auto pass2 = new SecondSemanticPass(modul);
183     pass2.run();
184   }
185 
186   /// Loads a module by its file path and runs pass 1 on it.
187   /// Params:
188   ///   filePath = E.g.: src/main.d
189   void importModuleByFile(cstring filePath)
190   {
191     if (mm.moduleByPath(filePath))
192       return; // The module has already been loaded.
193 
194     lzy(log(LogLoad, filePath));
195 
196     if (auto modul = mm.loadModuleFile(filePath))
197       runPass1(modul); // Load and run pass 1 on it.
198     else
199       mm.errorModuleNotFound(filePath);
200   }
201 
202   /// Loads a module by its FQN path and runs pass 1 on it.
203   /// Params:
204   ///   modFQNPath = E.g.: dil/cmd/Compile
205   ///   fqnTok = Identifier token in the import statement.
206   ///   modul = Where the import statement is located.
207   void importModuleByFQN(cstring modFQNPath,
208     Token* fqnTok = null, Module modul = null)
209   {
210     if (mm.moduleByFQN(modFQNPath))
211       return; // The module has already been loaded.
212     if (auto modFilePath = mm.findModuleFile(modFQNPath))
213     {
214       lzy(log(LogImport, modFQNPath.replace('/', '.'), modFilePath));
215       runPass1(mm.loadModule(modFQNPath)); // Load and run pass 1 on it.
216     }
217     else
218       mm.errorModuleNotFound(modFQNPath ~ ".d",
219         fqnTok ? fqnTok.getErrorLocation(modul.filePath()) : null);
220   }
221 
222   void importModuleByDecl(ImportDecl d, Module modul)
223   {
224     foreach (i, fqnPath; d.getModuleFQNs(dirSep))
225       importModuleByFQN(fqnPath, d.moduleFQNs[i][0], modul);
226   }
227 
228   /// Runs the command.
229   override void run()
230   {
231     mm = new ModuleManager(cc);
232 
233     // Always implicitly import "object.d".
234     importModuleByFQN("object");
235     auto objectModule = mm.moduleByFQN("object");
236     if (!objectModule)
237     {} // error
238 
239     // Load modules specified on the command line.
240     foreach (filePath; filePaths)
241       importModuleByFile(filePath);
242 
243     // Run pass2 on all the loaded modules.
244     foreach (m; mm.orderedModules)
245     {
246       runPass2(m);
247     }
248 
249     lzy({if (cc.diag.hasInfo()) log(LogDiags);}());
250     //lzy(cc.diag.hasInfo() && log(LogDiags)); // DMD bug: void has no value
251   }
252 }