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 91 void isGreaterDouble(){ 92 _stack.push(NaData(_stack.pop.doubleVal > _stack.pop.doubleVal)); 93 } 94 void isGreaterSameDouble(){ 95 _stack.push(NaData(_stack.pop.doubleVal >= _stack.pop.doubleVal)); 96 } 97 98 void binaryNot(){ 99 _stack.push(NaData(!_stack.pop.boolVal)); 100 } 101 void binaryAnd(){ 102 _stack.push(NaData(_stack.pop.boolVal && _stack.pop.boolVal)); 103 } 104 void binaryOr(){ 105 _stack.push(NaData(_stack.pop.boolVal || _stack.pop.boolVal)); 106 } 107 108 void push(){ 109 _stack.push(*_arg); 110 } 111 void pushFromAbs(){ 112 _stack.push(_stack.read(_arg.intVal)); 113 } 114 void pushRefFromAbs(){ 115 _stack.push(NaData(_stack.readPtr(_arg.intVal))); 116 } 117 void writeToAbs(){ 118 _stack.write(_arg.intVal,_stack.pop); 119 } 120 void pushFrom(){ 121 _stack.push(_stack.read(_arg.intVal + _stackIndex)); 122 } 123 void pushRefFrom(){ 124 _stack.push(NaData(_stack.readPtr(_arg.intVal + _stackIndex))); 125 } 126 void writeTo(){ 127 _stack.write(_arg.intVal + _stackIndex, _stack.pop); 128 } 129 void pop(){ 130 _stack.pop; 131 } 132 void popN(){ 133 _stack.pop(_arg.intVal); 134 } 135 136 void writeToRef(){ 137 // Left side is evaluated first 138 *(_stack.pop.ptrVal) = _stack.pop; 139 } 140 void deref(){ 141 _stack.push(*(_stack.pop.ptrVal)); 142 } 143 void incRef(){ 144 _stack.push(NaData(_stack.pop.ptrVal + _stack.pop.intVal)); 145 } 146 147 void doIf(){ 148 if (_stack.pop.boolVal == false){ 149 _inst++; 150 _arg++; 151 } 152 } 153 void jump(){ 154 _inst = &(_instructions)[_arg.intVal] - 1; 155 _arg = &(_arguments)[_arg.intVal] - 1; 156 } 157 void jumpFrame(){ 158 _jumpStack.push(StackFrame(_inst, _arg, _stackIndex)); 159 _inst = &(_instructions)[_arg.intVal] - 1; 160 _arg = &(_arguments)[_arg.intVal] - 1; 161 _stackIndex = _stack.count; 162 } 163 void jumpBack(){ 164 if (_jumpStack.count){ 165 StackFrame frame = _jumpStack.pop; 166 _inst = frame.instruction; 167 _arg = frame.argument; 168 _stack.peek(_stackIndex); 169 _stackIndex = frame.stackIndex; 170 return; 171 } 172 _inst = &(_instructions)[$-1] + 1; 173 } 174 175 void makeArray(){ 176 NaData array; 177 array.makeArray(_stack.pop.intVal); 178 _stack.push(array); 179 } 180 void arrayLength(){ 181 _stack.push(NaData(_stack.pop.arrayValLength)); 182 } 183 void arrayLengthSet(){ 184 NaData array = _stack.pop; 185 array.arrayValLength = _stack.pop.intVal; 186 _stack.push(array); 187 } 188 void isSameArray(){ 189 NaData[] a = _stack.pop.arrayVal, b = _stack.pop.arrayVal; 190 if (a.length != b.length){ 191 _stack.push(NaData(false)); 192 return; 193 } 194 NaData* aPtr = a.ptr, bPtr = b.ptr, aEnd = aPtr + a.length; 195 for (; aPtr < aEnd; ){ 196 if (aPtr.intVal != bPtr.intVal){ 197 _stack.push(NaData(false)); 198 return; 199 } 200 aPtr ++; 201 bPtr ++; 202 } 203 _stack.push(NaData(true)); 204 } 205 206 void intToDouble(){ 207 _stack.push(NaData(to!double(_stack.pop.intVal))); 208 } 209 void intToString(){ 210 _stack.push(NaData(to!dstring(_stack.pop.intVal))); 211 } 212 void boolToString(){ 213 _stack.push(NaData((_stack.pop.boolVal).to!dstring)); 214 } 215 void stringToBool(){ 216 _stack.push(NaData(_stack.pop.strVal == "true" ? true : false)); 217 } 218 void doubleToInt(){ 219 _stack.push(NaData(to!integer(_stack.pop.doubleVal))); 220 } 221 void doubleToString(){ 222 _stack.push(NaData(to!dstring(_stack.pop.doubleVal))); 223 } 224 void stringToInt(){ 225 _stack.push(NaData(to!integer(_stack.pop.strVal))); 226 } 227 void stringToDouble(){ 228 _stack.push(NaData(to!double(_stack.pop.strVal))); 229 } 230 public: 231 /// constructor 232 this(uinteger stackLength = 65_536){ 233 // prepare instruction table, forget codes, will do them in a loop after 234 _instructionTable = [ 235 NaInstruction("mathAddInt",0,2,1,&mathAddInt), 236 NaInstruction("mathSubtractInt",0,2,1,&mathSubtractInt), 237 NaInstruction("mathMultiplyInt",0,2,1,&mathMultiplyInt), 238 NaInstruction("mathDivideInt",0,2,1,&mathDivideInt), 239 NaInstruction("mathModInt",0,2,1,&mathModInt), 240 NaInstruction("mathAddDouble",0,2,1,&mathAddDouble), 241 NaInstruction("mathSubtractDouble",0,2,1,&mathSubtractDouble), 242 NaInstruction("mathMultiplyDouble",0,2,1,&mathMultiplyDouble), 243 NaInstruction("mathDivideDouble",0,2,1,&mathDivideDouble), 244 NaInstruction("mathModDouble",0,2,1,&mathModDouble), 245 NaInstruction("isSame",0,2,1,&isSame), 246 NaInstruction("isGreaterInt",0,2,1,&isGreaterInt), 247 NaInstruction("isGreaterSameInt",0,2,1,&isGreaterSameInt), 248 NaInstruction("isGreaterDouble",0,2,1,&isGreaterDouble), 249 NaInstruction("isGreaterSameDouble",0,2,1,&isGreaterSameDouble), 250 NaInstruction("not",0,1,1,&binaryNot), 251 NaInstruction("and",0,2,1,&binaryAnd), 252 NaInstruction("or",0,2,1,&binaryOr), 253 NaInstruction("push",0,true,0,1,&push), 254 NaInstruction("pushFrom",0,true,0,1,&pushFrom), 255 NaInstruction("pushRefFrom",0,true,0,1,&pushRefFrom), 256 NaInstruction("writeTo",0,true,1,0,&writeTo), 257 NaInstruction("pushFromAbs",0,true,0,1,&pushFromAbs), 258 NaInstruction("pushRefFromAbs",0,true,0,1,&pushRefFromAbs), 259 NaInstruction("writeToAbs",0,true,1,0,&writeToAbs), 260 NaInstruction("pop",0,1,0,&pop), 261 NaInstruction("popN",0,true,255,0,&popN), 262 NaInstruction("writeToRef",0,2,0,&writeToRef), 263 NaInstruction("deref",0,1,1,&deref), 264 NaInstruction("incRef",0,2,1,&incRef), 265 NaInstruction("if",0,1,0,&doIf), 266 NaInstruction("jump",0,true,true,0,0,&jump), 267 NaInstruction("jumpFrame",0,true,true,0,0,&jumpFrame), 268 NaInstruction("jumpBack",0,&jumpBack), 269 NaInstruction("makeArray",0,1,1,&makeArray), 270 NaInstruction("arrayLength",0,1,1,&arrayLength), 271 NaInstruction("arrayLengthSet",0,2,1,&arrayLengthSet), 272 NaInstruction("isSameArray",0,2,1,&isSameArray), 273 NaInstruction("intToDouble",0,1,1,&intToDouble), 274 NaInstruction("intToString",0,1,1,&intToString), 275 NaInstruction("boolToString",0,1,1,&boolToString), 276 NaInstruction("stringToBool",0,1,1,&stringToBool), 277 NaInstruction("doubleToInt",0,1,1,&doubleToInt), 278 NaInstruction("doubleToString",0,1,1,&doubleToString), 279 NaInstruction("stringToInt",0,1,1,&stringToInt), 280 NaInstruction("stringToDouble",0,1,1,&stringToDouble), 281 ]; 282 // now assign codes 283 foreach (i; 0 .. _instructionTable.length) 284 _instructionTable[i].code = cast(ushort)i; 285 // prepare stack 286 _stack = new ArrayStack!NaData(stackLength); 287 _jumpStack = new ArrayStack!StackFrame; 288 } 289 /// destructor 290 ~this(){ 291 .destroy(_stack); 292 .destroy(_jumpStack); 293 } 294 /// Loads bytecode into VM 295 /// 296 /// Returns: errors in a string[], or [] if no errors 297 string[] load(string[] byteCode){ 298 NaBytecode bcode = new NaBytecode(_instructionTable.dup); 299 string[] r = bcode.readByteCode(byteCode); 300 if (r.length) 301 return r; 302 r = bcode.resolve(); 303 if (r.length) 304 return r; 305 _instructions = bcode.getBytecodePointers(); 306 try{ 307 _arguments = bcode.getArgumentsNaData(); 308 }catch (Exception e){ 309 string msg = e.msg; 310 .destroy(e); 311 return [msg]; 312 } 313 return []; 314 } 315 /// ditto 316 string[] load(NaBytecode byteCode){ 317 string[] r = byteCode.resolve(); 318 if (r.length) 319 return r; 320 _instructions = byteCode.getBytecodePointers(); 321 try{ 322 _arguments = byteCode.getArgumentsNaData(); 323 }catch (Exception e){ 324 string msg = e.msg; 325 .destroy(e); 326 return [msg]; 327 } 328 return []; 329 } 330 331 /// Adds a new instruction 332 /// 333 /// Returns: true on success, false if not (pointer might be null, code might be already in use, name might already be in use) 334 bool addInstruction(NaInstruction instruction, ref string error){ 335 if (instruction.pointer is null){ 336 error = "instruction pointer cannot be null"; 337 return false; 338 } 339 instruction.name = instruction.name.lowercase(); 340 foreach (inst; _instructionTable){ 341 if (instruction.name == inst.name){ 342 error = "instruction name, "~inst.name~", already exists"; 343 return false; 344 } 345 if (instruction.code == inst.code){ 346 error = "instruction code, "~inst.code.to!string~", already exists"; 347 return false; 348 } 349 } 350 _instructionTable ~= instruction; 351 return true; 352 } 353 /// ditto 354 bool addInstruction(NaInstruction instruction){ 355 string error; 356 return addInstruction(instruction, error); 357 } 358 /// Clears stack 359 void clearStack(){ 360 if (_stack.count) 361 _stack.pop(_stack.count); 362 } 363 /// Starts execution of byte code, starting with the instruction at `index` 364 void execute(uinteger index = 0){ 365 if (index >= _instructions.length) 366 return; 367 _inst = &(_instructions[index]); 368 _arg = &(_arguments[index]); 369 const void delegate()* lastInst = &_instructions[$-1]+1; 370 do{ 371 (*_inst)(); 372 _inst++; 373 _arg++; 374 }while (_inst < lastInst); 375 } 376 }