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. A lot
22  * of extra code is added to capture the result of the previous expression, but all one really needs is
23  * a call to evaluate or evaluateFile and runVMFibers call surrounded by a try-catch.
24  */
25 void evaluateWithErrorChecking(Terminal* term, Interpreter interpreter, in string source, in string fileName)
26 {
27     try 
28     {
29         ScriptAny result;
30         if(source == "" && fileName != "<stdin>")
31             result = interpreter.evaluateFile(fileName);
32         else
33         {
34             result = interpreter.evaluate(source);
35             if(result != ScriptAny.UNDEFINED)
36             {
37                 interpreter.forceSetGlobal("_", result, false);
38             }
39             else
40             {
41                 auto _ = interpreter.vm.lastValuePopped;
42                 if(_ != ScriptAny.UNDEFINED)
43                     interpreter.forceSetGlobal("_", _, false);
44                 term.color(Color.green, Color.DEFAULT);
45                 if(_ != ScriptAny.UNDEFINED)
46                     term.writeln(_);
47                 term.color(Color.DEFAULT, Color.DEFAULT);
48             }
49         }
50         interpreter.runVMFibers();
51         if(source != "")
52             term.writeln("The program successfully returned " ~ result.toString);
53     }
54     catch(ScriptCompileException ex)
55     {
56         stderr.writeln("\nIn file " ~ fileName);
57         stderr.writefln("%s", ex);
58     }
59     catch(ScriptRuntimeException ex)
60     {
61         stderr.writeln("\nIn file " ~ fileName);
62         stderr.writefln("%s", ex);
63         if(ex.thrownValue.type != ScriptAny.Type.UNDEFINED)
64             stderr.writefln("Value thrown: %s", ex.thrownValue);
65     }
66     catch(Compiler.UnimplementedException ex)
67     {
68         stderr.writeln(ex.msg);
69     }
70 }
71 
72 private void printUsage()
73 {
74     stderr.writeln("Usage: dmildew_run <scriptfile> [options]");
75     stderr.writeln("       dmildew_run [options]");
76     stderr.writeln("Options: -v|--verbose Prints very verbose bytecode execution information");
77     stderr.writeln("         -d|--disasm  Prints the disassembly of bytecode before execution");
78     stderr.writeln("         -h|--help    Print this usage message");
79 }
80 
81 /**
82  * Main function for the REPL or interpreter. If no command line arguments are specified, it enters
83  * interactive REPL mode, otherwise it attempts to execute the first argument as a script file.
84  */
85 int main(string[] args)
86 {
87     Terminal terminal = Terminal(ConsoleOutputType.linear);
88     // auto terminal = Terminal(ConsoleOutputType.linear);
89     bool printVMDebugInfo = false;
90     bool printDisasm = false;
91 
92     try 
93     {
94         auto options = cast(immutable)getopt(args, 
95                 "verbose|v", &printVMDebugInfo,
96                 "disasm|d", &printDisasm);
97         if(options.helpWanted) 
98         {
99             printUsage();
100             return 1;
101         }
102     }
103     catch(Exception ex)
104     {
105         printUsage();
106         return 64;
107     }
108 
109     auto interpreter = new Interpreter(printDisasm, printVMDebugInfo);
110     interpreter.initializeStdlib();
111 
112     if(args.length > 1)
113     {
114         string[] fileNames = args[1..$];
115         foreach(fileName ; fileNames)
116             evaluateWithErrorChecking(&terminal, interpreter, "", fileName);
117     }    
118     else
119     {
120         while(true)
121         {
122             try 
123             {
124                 // TODO run input from another thread to establish event loop
125                 string input = strip(terminal.getline("mildew> "));
126                 if(input == "#exit" || input == "")
127                     break;
128                 while(input.length > 0 && input[$-1]=='\\')
129                 {
130                     input = input[0..$-1];
131                     input ~= "\n" ~ strip(terminal.getline(">>> "));
132                 }
133                 // terminal.writeln(":");
134                 stdout.writeln();
135                 evaluateWithErrorChecking(&terminal, interpreter, input, "<stdin>");
136             }
137             catch(UserInterruptionException ex)
138             {
139                 break;
140             }
141         }
142         terminal.writeln("");
143     }
144     return 0;
145 }