1 module commands; 2 import pegged.grammar; 3 import commands.grammar; 4 import commands.uda; 5 import core.thread; 6 import core.sync.mutex; 7 import std.stdio, std..string; 8 import std.format; 9 import std.traits; 10 11 __gshared CommandInterpreter gCommandInterpreter; 12 13 class CommandReaderThread : Thread { 14 __gshared bool terminate; 15 this() { 16 super(&run); 17 } 18 19 private: 20 void cls() { 21 writeln("\033[2J"); 22 } 23 24 void run() { 25 string line; 26 while (!terminate) { 27 stdout.writef("> "); 28 line = stdin.readln().strip(); 29 if (line is null) { 30 writeln("Caught EOF"); 31 terminate = true; 32 } else { 33 gCommandInterpreter.interpret(line); 34 } 35 } 36 } 37 } 38 39 class CommandInterpreter { 40 private { 41 CommandReaderThread reader; 42 CmdTableEntry[string][string] commandTable; 43 } 44 45 void interpret(string line) { 46 auto tree = CommandParser(line); 47 48 if (!tree.successful) { 49 writeln("unable to parse"); 50 return; 51 } 52 53 bool caughtException = false; 54 55 string parseToChild(ParseTree c) { 56 switch (c.name) { 57 case "CommandParser": 58 case "CommandParser.Primary": 59 case "CommandParser.ArgTypes": 60 return parseToChild(c.children[0]); 61 case "CommandParser.FunctionCall": 62 import std..string : split; 63 string[] args; 64 string ns = "global", func; 65 auto id = c.children[0]; 66 if (id.children[0].name == "CommandParser.FunctionNamespace") { 67 ns = parseToChild(id.children[0]); 68 func = parseToChild(id.children[1]); 69 } else { 70 func = parseToChild(id.children[0]); 71 } 72 if (c.children.length == 2) { 73 args = parseToChild(c.children[1]).split(','); 74 } 75 76 // bailout, avoid execution 77 if (caughtException) { 78 return ""; 79 } 80 81 // writeln("Hit func in namespace ", ns, " called ", func, " with args ", args); 82 try { 83 return eval(ns, func, args); 84 } catch (Exception e) { 85 writeln("Caught exception: ", e.msg); 86 caughtException = true; 87 return ""; 88 } 89 90 case "CommandParser.Function": 91 case "CommandParser.FunctionNamespace": 92 case "CommandParser.Number": 93 case "CommandParser.String": 94 return c.matches[0]; 95 case "CommandParser.HexLiteral": 96 return c.matches[0] ~ c.matches[1]; 97 case "CommandParser.EmptyArgs": 98 return ""; 99 case "CommandParser.Args": 100 // ugh, I hate this, but I have to join them 101 import std.conv : text; 102 import std.algorithm.iteration : joiner, each; 103 104 auto list = c.children[0].children; 105 string[] vals; 106 list.each!((p) => vals ~= parseToChild(p)); 107 return vals.joiner(",").text; 108 default: 109 assert(0, "Unhandled " ~ c.name); 110 } 111 } 112 113 // writeln(tree); 114 115 parseToChild(tree); 116 } 117 118 string eval(string func, string[] args) { 119 return eval("global", func, args); 120 } 121 122 string eval(string ns, string func, string[] args) { 123 if (auto possible = ns in commandTable) { 124 // safe deref since assignment checks if it's null or not 125 if (auto _f = func in *possible) { 126 // ditto 127 auto f = *_f; 128 if (args.length < f.minArgs) { 129 throw new Exception(format!"Expected at least %d arguments, got %d\n"(f.minArgs, args.length)); 130 } 131 if (args.length > f.maxArgs) { 132 throw new Exception(format!"Expected at most %d arguments, got %d\n"(f.maxArgs, args.length)); 133 } 134 135 return f.cmd(args); 136 } 137 throw new Exception(format!"Could not find command %s\n"(func)); 138 } 139 throw new Exception(format!"Could not find namespace %s\n"(ns)); 140 } 141 142 143 void registerCommand(Command info, CommandType cmd) { 144 registerCommand("global", info, cmd); 145 } 146 147 void registerCommand(string ns, Command info, CommandType cmd) { 148 synchronized { 149 commandTable[ns][info.name] = CmdTableEntry(cmd, info.desc, info.minArgs, info.maxArgs); 150 } 151 } 152 153 /+ built-in functions +/ 154 string help(string[] args) { 155 import std.algorithm.sorting; 156 foreach(ns; commandTable.keys.sort!("a > b")) { 157 writeln("namespace ", ns, ":"); 158 foreach(entryName; commandTable[ns].keys.sort!("a < b")) { 159 auto entry = commandTable[ns][entryName]; 160 writef("\t%s:\n", entryName); 161 if (entry.desc != "") 162 writef("\t\t%s\n", entry.desc); 163 writef("\t\tmin args: %d, max args: %d\n", entry.minArgs, entry.maxArgs); 164 } 165 } 166 return ""; 167 } 168 169 string quit(string[] args) { 170 reader.terminate = true; 171 stdin.close(); 172 return ""; 173 } 174 175 void run() { 176 reader.run(); 177 } 178 179 void fork() { 180 reader.start(); 181 } 182 183 this() { 184 reader = new CommandReaderThread(); 185 } 186 187 ~this() { 188 reader.terminate = true; 189 } 190 191 shared static this() { 192 import std.stdio; 193 gCommandInterpreter = new CommandInterpreter(); 194 gCommandInterpreter.registerCommand(Command("help", "Display a listing of every registered function"), &gCommandInterpreter.help); 195 gCommandInterpreter.registerCommand(Command("quit", "Quit the REPL."), &gCommandInterpreter.quit); 196 } 197 198 } 199 200 201