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 }