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 = &regs_.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 = &regs_.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 }