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