Parser: issue better error msgs for missing closing tokens.
[dil.git] / src / main.d
blobab3ace29848283da5bc3a5f2feadf43055c86363
1 /// Author: Aziz Köksal
2 /// License: GPL3
3 /// $(Maturity average)
4 module main;
6 import dil.parser.Parser;
7 import dil.lexer.Lexer,
8 dil.lexer.Token;
9 import dil.ast.Declarations,
10 dil.ast.Expressions,
11 dil.ast.Node,
12 dil.ast.Visitor;
13 import dil.semantic.Module,
14 dil.semantic.Symbols,
15 dil.semantic.Pass1,
16 dil.semantic.Pass2,
17 dil.semantic.Passes;
18 import dil.code.Interpreter;
19 import dil.translator.German;
20 import dil.Messages;
21 import dil.CompilerInfo;
22 import dil.Diagnostics;
23 import dil.SourceText;
24 import dil.Compilation;
26 import cmd.Compile;
27 import cmd.Highlight;
28 import cmd.Statistics;
29 import cmd.ImportGraph;
30 import cmd.DDoc;
32 import Settings;
33 import SettingsLoader;
34 import common;
36 import Integer = tango.text.convert.Integer;
37 import tango.stdc.stdio;
38 import tango.io.File;
39 import tango.text.Util;
40 import tango.time.StopWatch;
41 import tango.text.Ascii : icompare;
43 /// Entry function of dil.
44 void main(char[][] args)
46 auto diag = new Diagnostics();
47 ConfigLoader(diag).load();
48 if (diag.hasInfo)
49 return printErrors(diag);
51 if (args.length <= 1)
52 return printHelp("main");
54 string command = args[1];
55 switch (command)
57 case "c", "compile":
58 if (args.length < 3)
59 return printHelp(command);
61 CompileCommand cmd;
62 cmd.context = newCompilationContext();
63 cmd.diag = diag;
65 foreach (arg; args[2..$])
67 if (parseDebugOrVersion(arg, cmd.context))
69 else if (strbeg(arg, "-I"))
70 cmd.context.importPaths ~= arg[2..$];
71 else if (arg == "-release")
72 cmd.context.releaseBuild = true;
73 else if (arg == "-unittest")
75 version(D2)
76 cmd.context.addVersionId("unittest");
77 cmd.context.unittestBuild = true;
79 else if (arg == "-d")
80 cmd.context.acceptDeprecated = true;
81 else if (arg == "-ps")
82 cmd.printSymbolTree = true;
83 else if (arg == "-pm")
84 cmd.printModuleTree = true;
85 else
86 cmd.filePaths ~= arg;
88 cmd.run();
89 diag.hasInfo && printErrors(diag);
90 break;
91 case "ddoc", "d":
92 if (args.length < 4)
93 return printHelp(command);
95 DDocCommand cmd;
96 cmd.destDirPath = args[2];
97 cmd.macroPaths = GlobalSettings.ddocFilePaths;
98 cmd.context = newCompilationContext();
99 cmd.diag = diag;
101 // Parse arguments.
102 foreach (arg; args[3..$])
104 if (parseDebugOrVersion(arg, cmd.context))
106 else if (arg == "--xml")
107 cmd.writeXML = true;
108 else if (arg == "-i")
109 cmd.includeUndocumented = true;
110 else if (arg == "-v")
111 cmd.verbose = true;
112 else if (arg.length > 3 && strbeg(arg, "-m="))
113 cmd.modsTxtPath = arg[3..$];
114 else if (arg.length > 5 && icompare(arg[$-4..$], "ddoc") == 0)
115 cmd.macroPaths ~= arg;
116 else
117 cmd.filePaths ~= arg;
119 cmd.run();
120 diag.hasInfo && printErrors(diag);
121 break;
122 case "hl", "highlight":
123 if (args.length < 3)
124 return printHelp(command);
126 HighlightCommand cmd;
127 cmd.diag = diag;
129 foreach (arg; args[2..$])
131 switch (arg)
133 case "--syntax":
134 cmd.add(HighlightCommand.Option.Syntax); break;
135 case "--xml":
136 cmd.add(HighlightCommand.Option.XML); break;
137 case "--html":
138 cmd.add(HighlightCommand.Option.HTML); break;
139 case "--lines":
140 cmd.add(HighlightCommand.Option.PrintLines); break;
141 default:
142 cmd.filePath = arg;
145 cmd.run();
146 diag.hasInfo && printErrors(diag);
147 break;
148 case "importgraph", "igraph":
149 if (args.length < 3)
150 return printHelp(command);
152 IGraphCommand cmd;
153 cmd.context = newCompilationContext();
155 foreach (arg; args[2..$])
157 if (parseDebugOrVersion(arg, cmd.context))
159 else if (strbeg(arg, "-I"))
160 cmd.context.importPaths ~= arg[2..$];
161 else if(strbeg(arg, "-x"))
162 cmd.regexps ~= arg[2..$];
163 else if(strbeg(arg, "-l"))
164 cmd.levels = Integer.toInt(arg[2..$]);
165 else if(strbeg(arg, "-si"))
166 cmd.siStyle = arg[3..$];
167 else if(strbeg(arg, "-pi"))
168 cmd.piStyle = arg[3..$];
169 else
170 switch (arg)
172 case "--dot":
173 cmd.add(IGraphCommand.Option.PrintDot); break;
174 case "--paths":
175 cmd.add(IGraphCommand.Option.PrintPaths); break;
176 case "--list":
177 cmd.add(IGraphCommand.Option.PrintList); break;
178 case "-i":
179 cmd.add(IGraphCommand.Option.IncludeUnlocatableModules); break;
180 case "-hle":
181 cmd.add(IGraphCommand.Option.HighlightCyclicEdges); break;
182 case "-hlv":
183 cmd.add(IGraphCommand.Option.HighlightCyclicVertices); break;
184 case "-gbp":
185 cmd.add(IGraphCommand.Option.GroupByPackageNames); break;
186 case "-gbf":
187 cmd.add(IGraphCommand.Option.GroupByFullPackageName); break;
188 case "-m":
189 cmd.add(IGraphCommand.Option.MarkCyclicModules); break;
190 default:
191 cmd.filePath = arg;
194 cmd.run();
195 break;
196 case "stats", "statistics":
197 if (args.length < 3)
198 return printHelp(command);
200 StatsCommand cmd;
201 foreach (arg; args[2..$])
202 if (arg == "--toktable")
203 cmd.printTokensTable = true;
204 else if (arg == "--asttable")
205 cmd.printNodesTable = true;
206 else
207 cmd.filePaths ~= arg;
208 cmd.run();
209 break;
210 case "tok", "tokenize":
211 if (args.length < 3)
212 return printHelp(command);
213 SourceText sourceText;
214 char[] filePath;
215 char[] separator;
216 bool ignoreWSToks;
217 bool printWS;
219 foreach (arg; args[2..$])
221 if (strbeg(arg, "-s"))
222 separator = arg[2..$];
223 else if (arg == "-")
224 sourceText = new SourceText("stdin", readStdin());
225 else if (arg == "-i")
226 ignoreWSToks = true;
227 else if (arg == "-ws")
228 printWS = true;
229 else
230 filePath = arg;
233 separator || (separator = "\n");
234 if (!sourceText)
235 sourceText = new SourceText(filePath, true);
237 diag = new Diagnostics();
238 auto lx = new Lexer(sourceText, diag);
239 lx.scanAll();
240 auto token = lx.firstToken();
242 for (; token.kind != TOK.EOF; token = token.next)
244 if (token.kind == TOK.Newline || ignoreWSToks && token.isWhitespace)
245 continue;
246 if (printWS && token.ws)
247 Stdout(token.wsChars);
248 Stdout(token.srcText)(separator);
251 diag.hasInfo && printErrors(diag);
252 break;
253 case "trans", "translate":
254 if (args.length < 3)
255 return printHelp(command);
257 if (args[2] != "German")
258 return Stdout.formatln("Error: unrecognized target language \"{}\"", args[2]);
260 diag = new Diagnostics();
261 auto filePath = args[3];
262 auto mod = new Module(filePath, diag);
263 // Parse the file.
264 mod.parse();
265 if (!mod.hasErrors)
266 { // Translate
267 auto german = new GermanTranslator(Stdout, " ");
268 german.translate(mod.root);
270 printErrors(diag);
271 break;
272 case "profile":
273 if (args.length < 3)
274 break;
275 char[][] filePaths;
276 if (args[2] == "dstress")
278 auto text = cast(char[])(new File("dstress_files")).read();
279 filePaths = split(text, "\0");
281 else
282 filePaths = args[2..$];
284 StopWatch swatch;
285 swatch.start;
287 foreach (filePath; filePaths)
288 (new Lexer(new SourceText(filePath, true))).scanAll();
290 Stdout.formatln("Scanned in {:f10}s.", swatch.stop);
291 break;
292 case "?", "help":
293 printHelp(args.length >= 3 ? args[2] : "");
294 break;
295 default:
296 printHelp("main");
300 /// Reads the standard input and returns its contents.
301 char[] readStdin()
303 char[] text;
304 while (1)
306 auto c = getc(stdin);
307 if (c == EOF)
308 break;
309 text ~= c;
311 return text;
314 /// Available commands.
315 const char[] COMMANDS =
316 " help (?)\n"
317 " compile (c)\n"
318 " ddoc (d)\n"
319 " highlight (hl)\n"
320 " importgraph (igraph)\n"
321 " statistics (stats)\n"
322 " tokenize (tok)\n"
323 " translate (trans)\n";
325 bool strbeg(char[] str, char[] begin)
327 if (str.length >= begin.length)
329 if (str[0 .. begin.length] == begin)
330 return true;
332 return false;
335 /// Creates the global compilation context.
336 CompilationContext newCompilationContext()
338 auto cc = new CompilationContext;
339 cc.importPaths = GlobalSettings.importPaths;
340 cc.addVersionId("dil");
341 cc.addVersionId("all");
342 version(D2)
343 cc.addVersionId("D_Version2");
344 foreach (versionId; GlobalSettings.versionIds)
345 if (Lexer.isValidUnreservedIdentifier(versionId))
346 cc.addVersionId(versionId);
347 return cc;
350 /// Parses a debug or version command line option.
351 bool parseDebugOrVersion(string arg, CompilationContext context)
353 if (strbeg(arg, "-debug"))
355 if (arg.length > 7)
357 auto val = arg[7..$];
358 if (isdigit(val[0]))
359 context.debugLevel = Integer.toInt(val);
360 else if (Lexer.isValidUnreservedIdentifier(val))
361 context.addDebugId(val);
363 else
364 context.debugLevel = 1;
366 else if (arg.length > 9 && strbeg(arg, "-version="))
368 auto val = arg[9..$];
369 if (isdigit(val[0]))
370 context.versionLevel = Integer.toInt(val);
371 else if (Lexer.isValidUnreservedIdentifier(val))
372 context.addVersionId(val);
374 else
375 return false;
376 return true;
379 /// Prints the errors collected in diag.
380 void printErrors(Diagnostics diag)
382 foreach (info; diag.info)
384 char[] errorFormat;
385 if (info.classinfo is LexerError.classinfo)
386 errorFormat = GlobalSettings.lexerErrorFormat;
387 else if (info.classinfo is ParserError.classinfo)
388 errorFormat = GlobalSettings.parserErrorFormat;
389 else if (info.classinfo is SemanticError.classinfo)
390 errorFormat = GlobalSettings.semanticErrorFormat;
391 else if (info.classinfo is Warning.classinfo)
392 errorFormat = "{0}: Warning: {3}";
393 else if (info.classinfo is dil.Information.Error.classinfo)
394 errorFormat = "Error: {3}";
395 else
396 continue;
397 auto err = cast(Problem)info;
398 Stderr.formatln(errorFormat, err.filePath, err.loc, err.col, err.getMsg);
402 /// Prints the help message of a command.
403 /// If the command wasn't found, the main help message is printed.
404 void printHelp(char[] command)
406 char[] msg;
407 switch (command)
409 case "c", "compile":
410 msg = `Compile D source files.
411 Usage:
412 dil compile file.d [file2.d, ...] [Options]
414 This command only parses the source files and does little semantic analysis.
415 Errors are printed to standard error output.
417 Options:
418 -d : accept deprecated code
419 -debug : include debug code
420 -debug=level : include debug(l) code where l <= level
421 -debug=ident : include debug(ident) code
422 -version=level : include version(l) code where l >= level
423 -version=ident : include version(ident) code
424 -Ipath : add 'path' to the list of import paths
425 -release : compile a release build
426 -unittest : compile a unittest build
427 -32 : produce 32 bit code (default)
428 -64 : produce 64 bit code
429 -ofPROG : output program to PROG
431 -ps : print the symbol tree of the modules
432 -pm : print the package/module tree
434 Example:
435 dil c src/main.d -Isrc/`;
436 break;
437 case "ddoc", "d":
438 msg = `Generate documentation from DDoc comments in D source files.
439 Usage:
440 dil ddoc Destination file.d [file2.d, ...] [Options]
442 Destination is the folder where the documentation files are written to.
443 Files with the extension .ddoc are recognized as macro definition files.
445 Options:
446 --xml : write XML instead of HTML documents
447 -i : include undocumented symbols
448 -v : verbose output
449 -m=PATH : write list of processed modules to PATH
451 Example:
452 dil d doc/ src/main.d src/macros_dil.ddoc -i -m=doc/modules.txt`;
453 break;
454 case "hl", "highlight":
455 // msg = GetMsg(MID.HelpGenerate);
456 msg = `Highlight a D source file with XML or HTML tags.
457 Usage:
458 dil hl file.d [Options]
460 Options:
461 --syntax : generate tags for the syntax tree
462 --xml : use XML format (default)
463 --html : use HTML format
464 --lines : print line numbers
466 Example:
467 dil hl src/main.d --html --syntax > main.html`;
468 break;
469 case "importgraph", "igraph":
470 // msg = GetMsg(MID.HelpImportGraph);
471 msg = `Parse a module and build a module dependency graph based on its imports.
472 Usage:
473 dil igraph file.d Format [Options]
475 The directory of file.d is implicitly added to the list of import paths.
477 Format:
478 --dot : generate a dot document (default)
479 Options related to --dot:
480 -gbp : Group modules by package names
481 -gbf : Group modules by full package name
482 -hle : highlight cyclic edges in the graph
483 -hlv : highlight modules in cyclic relationships
484 -siSTYLE : the edge style to use for static imports
485 -piSTYLE : the edge style to use for public imports
486 STYLE can be: "dashed", "dotted", "solid", "invis" or "bold"
488 --paths : print the file paths of the modules in the graph
490 --list : print the names of the module in the graph
491 Options common to --paths and --list:
492 -lN : print N levels.
493 -m : use '*' to mark modules in cyclic relationships
495 Options:
496 -Ipath : add 'path' to the list of import paths where modules are
497 looked for
498 -xREGEXP : exclude modules whose names match the regular expression
499 REGEXP
500 -i : include unlocatable modules
502 Example:
503 dil igraph src/main.d --list
504 dil igraph src/main.d | dot -Tpng > main.png`;
505 break;
506 case "tok", "tokenize":
507 msg = `Print the tokens of a D source file.
508 Usage:
509 dil tok file.d [Options]
511 Options:
512 - : reads text from the standard input.
513 -sSEPARATOR : print SEPARATOR instead of newline between tokens.
514 -i : ignore whitespace tokens (e.g. comments, shebang etc.)
515 -ws : print a token's preceding whitespace characters.
517 Example:
518 echo "module foo; void func(){}" | dil tok -
519 dil tok src/main.d | grep ^[0-9]`;
520 break;
521 case "stats", "statistics":
522 msg = "Gather statistics about D source files.
523 Usage:
524 dil stats file.d [file2.d, ...] [Options]
526 Options:
527 --toktable : print the count of all kinds of tokens in a table.
528 --asttable : print the count of all kinds of nodes in a table.
530 Example:
531 dil stats src/main.d src/dil/Unicode.d";
532 break;
533 case "trans", "translate":
534 msg = `Translate a D source file to another language.
535 Usage:
536 dil translate Language file.d
538 Languages that are supported:
539 *) German
541 Example:
542 dil trans German src/main.d`;
543 break;
544 case "main":
545 default:
546 auto COMPILED_WITH = __VENDOR__;
547 auto COMPILED_VERSION = Format("{}.{,:d3}", __VERSION__/1000, __VERSION__%1000);
548 auto COMPILED_DATE = __TIMESTAMP__;
549 msg = FormatMsg(MID.HelpMain, VERSION, COMMANDS, COMPILED_WITH,
550 COMPILED_VERSION, COMPILED_DATE);
552 Stdout(msg).newline;