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 }