1 module navm.navm; 2 3 import navm.defs; 4 import navm.bytecode; 5 6 import std.conv : to; 7 8 import utils.lists; 9 import utils.misc; 10 11 public alias NaData = navm.defs.NaData; 12 public alias NaInstruction = navm.bytecode.NaInstruction; 13 public alias readData = navm.bytecode.readData; 14 15 /// the VM 16 class NaVM{ 17 private: 18 NaInstruction[] _instructionTable; /// what instructions are what 19 protected: 20 void delegate()[] _instructions; /// instructions of loaded byte code 21 NaData[] _arguments; /// argument of each instruction 22 void delegate()* _inst; /// pointer to next instruction 23 NaData* _arg; /// pointer to next instruction's arguments 24 uinteger _stackIndex; /// stack index relative to which some instructions will pushFrom/writeTo 25 ArrayStack!NaData _stack; /// as the name says, stack 26 ArrayStack!StackFrame _jumpStack; /// for storing pointers before jumping 27 // instructions: 28 29 void mathAddInt(){ 30 _stack.push( 31 NaData(_stack.pop.intVal + _stack.pop.intVal) 32 ); 33 } 34 void mathSubtractInt(){ 35 _stack.push( 36 NaData(_stack.pop.intVal - _stack.pop.intVal) 37 ); 38 } 39 void mathMultiplyInt(){ 40 _stack.push( 41 NaData(_stack.pop.intVal * _stack.pop.intVal) 42 ); 43 } 44 void mathDivideInt(){ 45 _stack.push( 46 NaData(_stack.pop.intVal / _stack.pop.intVal) 47 ); 48 } 49 void mathModInt(){ 50 _stack.push( 51 NaData(_stack.pop.intVal % _stack.pop.intVal) 52 ); 53 } 54 55 void mathAddDouble(){ 56 _stack.push( 57 NaData(_stack.pop.doubleVal + _stack.pop.doubleVal) 58 ); 59 } 60 void mathSubtractDouble(){ 61 _stack.push( 62 NaData(_stack.pop.doubleVal - _stack.pop.doubleVal) 63 ); 64 } 65 void mathMultiplyDouble(){ 66 _stack.push( 67 NaData(_stack.pop.doubleVal * _stack.pop.doubleVal) 68 ); 69 } 70 void mathDivideDouble(){ 71 _stack.push( 72 NaData(_stack.pop.doubleVal / _stack.pop.doubleVal) 73 ); 74 } 75 void mathModDouble(){ 76 _stack.push( 77 NaData(_stack.pop.doubleVal % _stack.pop.doubleVal) 78 ); 79 } 80 81 void isSame(){ 82 _stack.push(NaData(_stack.pop.intVal == _stack.pop.intVal)); 83 } 84 void isGreaterInt(){ 85 _stack.push(NaData(_stack.pop.intVal > _stack.pop.intVal)); 86 } 87 void isGreaterSameInt(){ 88 _stack.push(NaData(_stack.pop.intVal >= _stack.pop.intVal)); 89 } 90 void isLesserInt(){ 91 _stack.push(NaData(_stack.pop.intVal < _stack.pop.intVal)); 92 } 93 void isLesserSameInt(){ 94 _stack.push(NaData(_stack.pop.intVal <= _stack.pop.intVal)); 95 } 96 97 void isGreaterDouble(){ 98 _stack.push(NaData(_stack.pop.doubleVal > _stack.pop.doubleVal)); 99 } 100 void isGreaterSameDouble(){ 101 _stack.push(NaData(_stack.pop.doubleVal >= _stack.pop.doubleVal)); 102 } 103 void isLesserDouble(){ 104 _stack.push(NaData(_stack.pop.doubleVal < _stack.pop.doubleVal)); 105 } 106 void isLesserSameDouble(){ 107 _stack.push(NaData(_stack.pop.doubleVal <= _stack.pop.doubleVal)); 108 } 109 110 void binaryNot(){ 111 _stack.push(NaData(!_stack.pop.boolVal)); 112 } 113 void binaryAnd(){ 114 _stack.push(NaData(_stack.pop.boolVal && _stack.pop.boolVal)); 115 } 116 void binaryOr(){ 117 _stack.push(NaData(_stack.pop.boolVal || _stack.pop.boolVal)); 118 } 119 120 void push(){ 121 _stack.push(*_arg); 122 } 123 void pushFromAbs(){ 124 _stack.push(_stack.readAbs(_arg.intVal)); 125 } 126 void pushRefFromAbs(){ 127 _stack.push(NaData(_stack.readPtrAbs(_arg.intVal))); 128 } 129 void writeToAbs(){ 130 _stack.writeAbs(_arg.intVal,_stack.pop); 131 } 132 void pushFrom(){ 133 _stack.push(_stack.read(_arg.intVal)); 134 } 135 void pushRefFrom(){ 136 _stack.push(NaData(_stack.readPtr(_arg.intVal))); 137 } 138 void writeTo(){ 139 _stack.write(_arg.intVal, _stack.pop); 140 } 141 void pop(){ 142 _stack.pop; 143 } 144 void popN(){ 145 _stack.pop(_arg.intVal); 146 } 147 148 void writeToRef(){ 149 // Left side is evaluated first 150 *(_stack.pop.ptrVal) = _stack.pop; 151 } 152 void deref(){ 153 _stack.push(*(_stack.pop.ptrVal)); 154 } 155 void incRef(){ 156 _stack.push(NaData(_stack.pop.ptrVal + _stack.pop.intVal)); 157 } 158 159 void doIf(){ 160 if (_stack.pop.boolVal == false){ 161 _inst++; 162 _arg++; 163 } 164 } 165 void jump(){ 166 _inst = (_instructions.ptr + _arg.intVal) -1; 167 _arg = (_arguments.ptr + _arg.intVal) -1; 168 } 169 void jumpFrame(){ 170 _jumpStack.push(StackFrame(_inst, _arg, _stackIndex)); 171 _inst = (_instructions.ptr + _arg.intVal) -1; 172 _arg = (_arguments.ptr + _arg.intVal) -1; 173 _stackIndex = _stack.count; 174 _stack.setIndex(_stackIndex); 175 } 176 void jumpBack(){ 177 if (_jumpStack.count){ 178 StackFrame frame = _jumpStack.pop; 179 _inst = frame.instruction; 180 _arg = frame.argument; 181 _stack.setIndex(frame.stackIndex); 182 _stack.peek(_stackIndex); 183 _stackIndex = frame.stackIndex; 184 return; 185 } 186 _inst = &(_instructions)[$-1] + 1; 187 } 188 189 void makeArray(){ 190 NaData array; 191 array.makeArray(_stack.pop.intVal); 192 _stack.push(array); 193 } 194 void arrayLength(){ 195 _stack.push(NaData(_stack.pop.arrayValLength)); 196 } 197 void arrayLengthSet(){ 198 NaData array = _stack.pop; 199 array.arrayValLength = _stack.pop.intVal; 200 _stack.push(array); 201 } 202 void isSameArray(){ 203 NaData[] a = _stack.pop.arrayVal, b = _stack.pop.arrayVal; 204 if (a.length != b.length){ 205 _stack.push(NaData(false)); 206 return; 207 } 208 NaData* aPtr = a.ptr, bPtr = b.ptr, aEnd = aPtr + a.length; 209 for (; aPtr < aEnd; ){ 210 if (aPtr.intVal != bPtr.intVal){ 211 _stack.push(NaData(false)); 212 return; 213 } 214 aPtr ++; 215 bPtr ++; 216 } 217 _stack.push(NaData(true)); 218 } 219 220 void intToDouble(){ 221 _stack.push(NaData(to!double(_stack.pop.intVal))); 222 } 223 void intToString(){ 224 _stack.push(NaData(to!dstring(_stack.pop.intVal))); 225 } 226 void boolToString(){ 227 _stack.push(NaData((_stack.pop.boolVal).to!dstring)); 228 } 229 void stringToBool(){ 230 _stack.push(NaData(_stack.pop.strVal == "true" ? true : false)); 231 } 232 void doubleToInt(){ 233 _stack.push(NaData(to!integer(_stack.pop.doubleVal))); 234 } 235 void doubleToString(){ 236 _stack.push(NaData(to!dstring(_stack.pop.doubleVal))); 237 } 238 void stringToInt(){ 239 _stack.push(NaData(to!integer(_stack.pop.strVal))); 240 } 241 void stringToDouble(){ 242 _stack.push(NaData(to!double(_stack.pop.strVal))); 243 } 244 public: 245 /// constructor 246 this(uinteger stackLength = 65_536){ 247 // prepare instruction table, forget codes, will do them in a loop after 248 _instructionTable = [ 249 NaInstruction("mathAddInt",0,2,1,&mathAddInt), 250 NaInstruction("mathSubtractInt",0,2,1,&mathSubtractInt), 251 NaInstruction("mathMultiplyInt",0,2,1,&mathMultiplyInt), 252 NaInstruction("mathDivideInt",0,2,1,&mathDivideInt), 253 NaInstruction("mathModInt",0,2,1,&mathModInt), 254 NaInstruction("mathAddDouble",0,2,1,&mathAddDouble), 255 NaInstruction("mathSubtractDouble",0,2,1,&mathSubtractDouble), 256 NaInstruction("mathMultiplyDouble",0,2,1,&mathMultiplyDouble), 257 NaInstruction("mathDivideDouble",0,2,1,&mathDivideDouble), 258 NaInstruction("mathModDouble",0,2,1,&mathModDouble), 259 NaInstruction("isSame",0,2,1,&isSame), 260 NaInstruction("isGreaterInt",0,2,1,&isGreaterInt), 261 NaInstruction("isGreaterSameInt",0,2,1,&isGreaterSameInt), 262 NaInstruction("isLesserInt",0,2,1,&isLesserInt), 263 NaInstruction("isLesserSameInt",0,2,1,&isLesserSameInt), 264 NaInstruction("isGreaterDouble",0,2,1,&isGreaterDouble), 265 NaInstruction("isGreaterSameDouble",0,2,1,&isGreaterSameDouble), 266 NaInstruction("isLesserDouble",0,2,1,&isLesserDouble), 267 NaInstruction("isLesserSameDouble",0,2,1,&isLesserSameDouble), 268 NaInstruction("not",0,1,1,&binaryNot), 269 NaInstruction("and",0,2,1,&binaryAnd), 270 NaInstruction("or",0,2,1,&binaryOr), 271 NaInstruction("push",0,true,0,1,&push), 272 NaInstruction("pushFrom",0,true,0,1,&pushFrom), 273 NaInstruction("pushRefFrom",0,true,0,1,&pushRefFrom), 274 NaInstruction("writeTo",0,true,1,0,&writeTo), 275 NaInstruction("pushFromAbs",0,true,0,1,&pushFromAbs), 276 NaInstruction("pushRefFromAbs",0,true,0,1,&pushRefFromAbs), 277 NaInstruction("writeToAbs",0,true,1,0,&writeToAbs), 278 NaInstruction("pop",0,1,0,&pop), 279 NaInstruction("popN",0,true,255,0,&popN), 280 NaInstruction("writeToRef",0,2,0,&writeToRef), 281 NaInstruction("deref",0,1,1,&deref), 282 NaInstruction("incRef",0,2,1,&incRef), 283 NaInstruction("if",0,1,0,&doIf), 284 NaInstruction("jump",0,true,true,0,0,&jump), 285 NaInstruction("jumpFrame",0,true,true,0,0,&jumpFrame), 286 NaInstruction("jumpBack",0,&jumpBack), 287 NaInstruction("makeArray",0,1,1,&makeArray), 288 NaInstruction("arrayLength",0,1,1,&arrayLength), 289 NaInstruction("arrayLengthSet",0,2,1,&arrayLengthSet), 290 NaInstruction("isSameArray",0,2,1,&isSameArray), 291 NaInstruction("intToDouble",0,1,1,&intToDouble), 292 NaInstruction("intToString",0,1,1,&intToString), 293 NaInstruction("boolToString",0,1,1,&boolToString), 294 NaInstruction("stringToBool",0,1,1,&stringToBool), 295 NaInstruction("doubleToInt",0,1,1,&doubleToInt), 296 NaInstruction("doubleToString",0,1,1,&doubleToString), 297 NaInstruction("stringToInt",0,1,1,&stringToInt), 298 NaInstruction("stringToDouble",0,1,1,&stringToDouble), 299 ]; 300 // now assign codes 301 foreach (i; 0 .. _instructionTable.length) 302 _instructionTable[i].code = cast(ushort)i; 303 // prepare stack 304 _stack = new ArrayStack!NaData(stackLength); 305 _jumpStack = new ArrayStack!StackFrame; 306 } 307 /// destructor 308 ~this(){ 309 .destroy(_stack); 310 .destroy(_jumpStack); 311 } 312 /// Loads bytecode into VM 313 /// 314 /// Returns: errors in a string[], or [] if no errors 315 string[] load(string[] byteCode){ 316 NaBytecode bcode = new NaBytecode(_instructionTable.dup); 317 string[] r = bcode.readByteCode(byteCode); 318 if (r.length) 319 return r; 320 r = bcode.resolve(); 321 if (r.length) 322 return r; 323 _instructions = bcode.getBytecodePointers(); 324 try{ 325 _arguments = bcode.getArgumentsNaData(); 326 }catch (Exception e){ 327 string msg = e.msg; 328 .destroy(e); 329 return [msg]; 330 } 331 return []; 332 } 333 /// ditto 334 string[] load(NaBytecode byteCode){ 335 string[] r = byteCode.resolve(); 336 if (r.length) 337 return r; 338 _instructions = byteCode.getBytecodePointers(); 339 try{ 340 _arguments = byteCode.getArgumentsNaData(); 341 }catch (Exception e){ 342 string msg = e.msg; 343 .destroy(e); 344 return [msg]; 345 } 346 return []; 347 } 348 /// a copy of the instructions table 349 @property NaInstruction[] instructionTable(){ 350 return _instructionTable.dup; 351 } 352 /// Adds a new instruction 353 /// 354 /// Returns: true on success, false if not (pointer might be null, code might be already in use, name might already be in use) 355 bool addInstruction(NaInstruction instruction, ref string error){ 356 if (instruction.pointer is null){ 357 error = "instruction pointer cannot be null"; 358 return false; 359 } 360 instruction.name = instruction.name.lowercase(); 361 foreach (inst; _instructionTable){ 362 if (instruction.name == inst.name){ 363 error = "instruction name, "~inst.name~", already exists"; 364 return false; 365 } 366 if (instruction.code == inst.code){ 367 error = "instruction code, "~inst.code.to!string~", already exists"; 368 return false; 369 } 370 } 371 _instructionTable ~= instruction; 372 return true; 373 } 374 /// ditto 375 bool addInstruction(NaInstruction instruction){ 376 string error; 377 return addInstruction(instruction, error); 378 } 379 /// Clears stack 380 void clearStack(){ 381 if (_stack.count) 382 _stack.pop(_stack.count); 383 } 384 /// Starts execution of byte code, starting with the instruction at `index` 385 void execute(uinteger index = 0){ 386 if (index >= _instructions.length) 387 return; 388 _inst = &(_instructions[index]); 389 _arg = &(_arguments[index]); 390 const void delegate()* lastInst = &_instructions[$-1]+1; 391 do{ 392 (*_inst)(); 393 _inst++; 394 _arg++; 395 }while (_inst < lastInst); 396 } 397 }