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 }