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 }