1 module vayne.vm; 2 3 4 import std.conv; 5 import std.datetime; 6 import std.range; 7 import std.stdio; 8 import std.string; 9 import std.traits; 10 11 12 import vayne.op; 13 import vayne.value; 14 import vayne.source.source; 15 16 public import vayne.value : Value; 17 18 19 enum VMOptions : uint { 20 PrintOutput = 1 << 0, 21 PrintOpCodes = 1 << 1, 22 DebugMode = PrintOutput | PrintOpCodes, 23 Default = 0, 24 } 25 26 27 class VMException : Exception { 28 this(string msg, string source, size_t line) { 29 super(msg, source, line); 30 } 31 } 32 33 34 private template isWriterObject(T) { 35 enum isWriterObject = isOutputRange!(T, char) || __traits(compiles, T.init.write("")); 36 } 37 38 39 struct VM(uint options = VMOptions.Default) { 40 static struct Error { 41 string msg; 42 string source; 43 size_t line; 44 } 45 alias ErrorHandler = void delegate(Error); 46 alias Globals = Value[string]; 47 48 void load(size_t registers, Value[] constants, const(Instr)[] instrs, const(SourceLoc)[] locs, const(string)[] sources) { 49 instrs_ = instrs; 50 locs_ = locs; 51 sources_ = sources; 52 consts_ = constants; 53 54 regs_.length = registers; 55 } 56 57 void load(size_t registers, size_t constants, const(Instr)[] instrs, const(SourceLoc)[] locs, const(string)[] sources) { 58 instrs_ = instrs; 59 locs_ = locs; 60 sources_ = sources; 61 62 regs_.length = registers; 63 consts_.length = constants; 64 } 65 66 void bindConst(T)(size_t index, ref T value) if (is(T == struct)) { 67 consts_[index] = Value(value); 68 } 69 70 void bindConst(T)(size_t index, in T value) if (!is(T == struct)) { 71 consts_[index] = Value(value); 72 } 73 74 void setGlobals(Globals globals) { 75 globals_ = globals; 76 } 77 78 void bindGlobals(Globals globals) { 79 foreach (k, v; globals) 80 globals_[k] = v; 81 } 82 83 void bindGlobal(T)(string name, ref T value) if (is(T == struct)) { 84 globals_[name] = Value(value); 85 } 86 87 void bindGlobal(T)(string name, in T value) if (!is(T == struct)) { 88 globals_[name] = Value(value); 89 } 90 91 @property void errorHandler(ErrorHandler handler) { 92 errorHandler_ = handler; 93 } 94 95 @property ErrorHandler errorHandler() const { 96 return errorHandler_; 97 } 98 99 void execute(T)(ref T output, Globals globals) if (isWriterObject!T) { 100 globals_ = globals; 101 102 execute(output); 103 } 104 105 void execute(T)(ref T output) if (isWriterObject!T) { 106 Instr instr = instrs_[0]; 107 size_t ip; 108 109 ref auto getArgV(size_t Arg)() if (Arg <= 3) { 110 if (instr.argConst!Arg) { 111 return consts_[instr.arg!Arg]; 112 } else { 113 return regs_[instr.arg!Arg]; 114 } 115 } 116 117 ref auto argBinaryOp(size_t A, string op, size_t B)() { 118 return getArgV!A.binaryOp!op(getArgV!B); 119 } 120 121 ref auto argCompareOp(size_t A, string op, size_t B)() { 122 return Value(getArgV!A.compareOp!op(getArgV!B)); 123 } 124 125 while (true) { 126 const op = instr.op; 127 128 static if (options & VMOptions.PrintOpCodes) { 129 writeln(op); 130 } 131 132 try { 133 Lswitch: final switch (op) with (OpCode) { 134 case Nop: 135 break; 136 case Halt: 137 return; 138 case Move: 139 regs_.ptr[instr.arg!0] = getArgV!1; 140 break; 141 case Throw: 142 throw new Exception(getArgV!0.toString); 143 case Output: 144 static if (isOutputRange!(T, char)) { 145 output.put(getArgV!0.get!string); 146 } else { 147 output.write(getArgV!0.get!string); 148 } 149 150 static if (options & VMOptions.PrintOutput) { 151 write(getArgV!0.get!string); 152 } 153 break; 154 case PushScope: 155 auto scope_ = getArgV!0; 156 switch (scope_.type) with (Value.Type) { 157 case Object: 158 case AssocArray: 159 scopes_ ~= getArgV!0; 160 break; 161 default: 162 throw new Exception(format("with statement expressions must be of type %s or %s, not %s", Object, AssocArray, scope_.type)); 163 } 164 break; 165 case PopScope: 166 scopes_.length = scopes_.length - instr.arg!0; 167 break; 168 case Minus: 169 regs_.ptr[instr.arg!0].unaryOp!"-"; 170 break; 171 case Not: 172 regs_.ptr[instr.arg!0] = Value(!regs_.ptr[instr.arg!0].get!bool); 173 break; 174 case Decrement: 175 regs_.ptr[instr.arg!0].unaryOp!"--"; 176 break; 177 case Increment: 178 regs_.ptr[instr.arg!0].unaryOp!"++"; 179 break; 180 case Jump: 181 ip = instr.arg!0; 182 instr = instrs_.ptr[ip]; 183 continue; 184 case JumpIfZero: 185 if (getArgV!1.get!long == 0) 186 goto case Jump; 187 break; 188 case JumpIfNotZero: 189 if (getArgV!1.get!long != 0) 190 goto case Jump; 191 break; 192 case And: 193 regs_.ptr[instr.arg!0] = argCompareOp!(1, "&&", 2); 194 break; 195 case Or: 196 regs_.ptr[instr.arg!0] = argCompareOp!(1, "||", 2); 197 break; 198 case Add: 199 regs_.ptr[instr.arg!0] = argBinaryOp!(1, "+", 2); 200 break; 201 case Subtract: 202 regs_.ptr[instr.arg!0] = argBinaryOp!(1, "-", 2); 203 break; 204 case Multiply: 205 regs_.ptr[instr.arg!0] = argBinaryOp!(1, "*", 2); 206 break; 207 case Divide: 208 regs_.ptr[instr.arg!0] = argBinaryOp!(1, "/", 2); 209 break; 210 case Remainder: 211 regs_.ptr[instr.arg!0] = argBinaryOp!(1, "%", 2); 212 break; 213 case Power: 214 regs_.ptr[instr.arg!0] = argBinaryOp!(1, "^^", 2); 215 break; 216 case Concat: 217 regs_.ptr[instr.arg!0] = getArgV!1.concatOp(getArgV!2); 218 break; 219 case Test: 220 regs_.ptr[instr.arg!0] = Value(getArgV!1.get!bool); 221 break; 222 case Equal: 223 regs_.ptr[instr.arg!0] = argCompareOp!(1, "==", 2); 224 break; 225 case NotEqual: 226 regs_.ptr[instr.arg!0] = argCompareOp!(1, "!=", 2); 227 break; 228 case Less: 229 regs_.ptr[instr.arg!0] = argCompareOp!(1, "<", 2); 230 break; 231 case LessOrEqual: 232 regs_.ptr[instr.arg!0] = argCompareOp!(1, "<=", 2); 233 break; 234 case Greater: 235 regs_.ptr[instr.arg!0] = argCompareOp!(1, ">", 2); 236 break; 237 case GreaterOrEqual: 238 regs_.ptr[instr.arg!0] = argCompareOp!(1, ">=", 2); 239 break; 240 case Length: 241 regs_.ptr[instr.arg!0] = Value(getArgV!1.length); 242 break; 243 case Keys: 244 regs_.ptr[instr.arg!0] = getArgV!1.keys(); 245 break; 246 case TestKey: 247 regs_.ptr[instr.arg!0] = Value(getArgV!1.has(getArgV!2)); 248 break; 249 case Slice: 250 regs_.ptr[instr.arg!0] = getArgV!1[getArgV!2..getArgV!3]; 251 break; 252 case Dispatch: 253 assert(!dispatchArg_); 254 255 auto name = getArgV!2; 256 auto pout = ®s_.ptr[instr.arg!0]; 257 258 auto obj = getArgV!1; 259 switch (obj.type) with (Value.Type) { 260 case Object: 261 case AssocArray: 262 if (getArgV!1.has(name, pout)) 263 break Lswitch; 264 break; 265 default: 266 break; 267 } 268 269 if (name.type == Value.Type.String) { 270 if (auto pvalue = name.get!string in globals_) { 271 if (pvalue.type == Value.Type.Function) { 272 dispatchArg_ = true; 273 *pout = *pvalue; 274 break Lswitch; 275 } 276 } 277 } 278 279 throw new Exception(format("dispatch failed for identifier '%s'", name.get!string)); 280 case Element: 281 regs_.ptr[instr.arg!0] = getArgV!1[getArgV!2]; 282 break; 283 case LookUp: 284 auto name = getArgV!1; 285 auto pout = ®s_.ptr[instr.arg!0]; 286 287 foreach_reverse (ref s; scopes_) { 288 if (s.has(name, pout)) 289 break Lswitch; 290 } 291 292 if (name.type == Value.Type.String) { 293 if (auto pvalue = name.get!string in globals_) { 294 *pout = *pvalue; 295 break Lswitch; 296 } 297 } 298 299 *pout = Value.init; 300 break; 301 case Call: 302 auto func = getArgV!1; 303 func.call(regs_.ptr[instr.arg!0], regs_.ptr[instr.arg!2..instr.arg!2 + instr.arg!3]); 304 break; 305 case DispatchCall: 306 auto func = getArgV!1; 307 if (dispatchArg_) { 308 func.call(regs_.ptr[instr.arg!0], regs_.ptr[instr.arg!2..instr.arg!2 + instr.arg!3]); 309 dispatchArg_ = false; 310 } else { 311 func.call(regs_.ptr[instr.arg!0], regs_.ptr[1 + instr.arg!2..instr.arg!2 + instr.arg!3]); 312 } 313 break; 314 } 315 316 if (++ip >= instrs_.length) 317 break; 318 instr = instrs_.ptr[ip]; 319 } catch (Throwable e) { 320 auto loc = locs_[ip]; 321 if (errorHandler_) { 322 errorHandler_(Error(e.msg, sources_[loc.id], loc.line)); 323 break; 324 } else { 325 throw new VMException(e.msg, sources_[loc.id], loc.line); 326 } 327 } 328 } 329 } 330 331 private: 332 Value[] consts_; 333 Value[] regs_; 334 Globals globals_; 335 Value[] scopes_; 336 bool dispatchArg_; 337 338 const(Instr)[] instrs_; 339 const(SourceLoc)[] locs_; 340 const(string)[] sources_; 341 342 ErrorHandler errorHandler_; 343 }