1 /**
2  * This module contains the main function for the REPL. It also runs script files.
3  */
4 module app;
5 
6 import std.file: readText, FileException;
7 import std.getopt;
8 import std.stdio;
9 import std..string: strip;
10 
11 import arsd.terminal;
12 
13 import mildew.compiler : Compiler;
14 import mildew.exceptions;
15 import mildew.interpreter;
16 import mildew.lexer;
17 import mildew.parser;
18 import mildew.types;
19 
20 /**
21  * This runs a script program and prints the appropriate error message when a script exception is caught.
22  */
23 void evaluateWithErrorChecking(Interpreter interpreter, in string source, in string fileName, bool printDisasm)
24 {
25     try 
26     {
27         ScriptAny result;
28         if(source == "" && fileName != "<stdin>")
29             result = interpreter.evaluateFile(fileName, printDisasm);
30         else
31             result = interpreter.evaluate(source, printDisasm);
32         writeln("The program successfully returned " ~ result.toString);
33     }
34     catch(ScriptCompileException ex)
35     {
36         stderr.writeln("In file " ~ fileName);
37         stderr.writefln("%s", ex);
38     }
39     catch(ScriptRuntimeException ex)
40     {
41         stderr.writeln("In file " ~ fileName);
42         stderr.writefln("%s", ex);
43         if(ex.thrownValue.type != ScriptAny.Type.UNDEFINED)
44             stderr.writefln("Value thrown: %s", ex.thrownValue);
45     }
46     catch(Compiler.UnimplementedException ex)
47     {
48         stderr.writeln(ex.msg);
49     }
50 }
51 
52 private void printUsage()
53 {
54     stderr.writeln("Usage: dmildew_run <scriptfile> [options]");
55     stderr.writeln("       dmildew_run [options]");
56     stderr.writeln("Options: -usevm : Use bytecode generation instead of tree walker (experimental)");
57     stderr.writeln("         -h     : Print this usage message");
58 }
59 
60 /**
61  * Main function for the REPL or interpreter. If no command line arguments are specified, it enters
62  * interactive REPL mode, otherwise it attempts to execute the first argument as a script file.
63  */
64 int main(string[] args)
65 {
66     auto terminal = Terminal(ConsoleOutputType.linear);
67     bool useVM = false;
68     bool printVMDebugInfo = false;
69     bool printDisasm = false;
70 
71     try 
72     {
73         auto options = cast(immutable)getopt(args, 
74                 "usevm|u", &useVM,
75                 "verbose|v", &printVMDebugInfo,
76                 "disasm|d", &printDisasm);
77         if(options.helpWanted) 
78         {
79             printUsage();
80             return 1;
81         }
82     }
83     catch(Exception ex)
84     {
85         printUsage();
86         return 64;
87     }
88 
89     auto interpreter = new Interpreter(useVM, printVMDebugInfo);
90     interpreter.initializeStdlib();
91 
92     if(args.length > 1)
93     {
94         string[] fileNames = args[1..$];
95         foreach(fileName ; fileNames)
96             evaluateWithErrorChecking(interpreter, "", fileName, printDisasm);
97     }    
98     else
99     {
100         while(true)
101         {
102             try 
103             {
104                 string input = strip(terminal.getline("mildew> "));
105                 if(input == "#exit" || input == "")
106                     break;
107                 while(input.length > 0 && input[$-1]=='\\')
108                 {
109                     input = input[0..$-1];
110                     input ~= "\n" ~ strip(terminal.getline(">>> "));
111                 }
112                 writeln();
113                 evaluateWithErrorChecking(interpreter, input, "<stdin>", printDisasm);
114             }
115             catch(UserInterruptionException ex)
116             {
117                 break;
118             }
119         }
120         writeln("");
121     }
122     return 0;
123 }