-
Notifications
You must be signed in to change notification settings - Fork 2
/
vm.js
98 lines (94 loc) · 3.05 KB
/
vm.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
const assemble = require('./assembler')
const {instructionToByteCode, byteCodeToName} = require('./byteCodes')
const vm = {
// add callStack to the world object
// move the localStack and localEnv into a frame object
// create an initial frame for the callStack object
// change localEnv and stack to be computed dynamically
evalParsedObject: w => {
// console.log(w.code);
w.ip = w.entry || 0;
w.print = w.print || console.log.bind(console);
w.bytecode = w.code[w.ip];
w.callStack = [{
name: 'main',
returnAddr: null,
localStack: [],
localEnv: {},
}];
Object.defineProperty(w, "localStack", { get: function () {
return w.callStack[w.callStack.length - 1].localStack;
}});
Object.defineProperty(w, "localEnv", { get: function () {
return w.callStack[w.callStack.length - 1].localEnv;
}});
while (w.bytecode != instructionToByteCode.HALT && w.ip < w.code.length) {
w.ip = w.ip + 1;
vm.dispatch(w);
w.bytecode = w.code[w.ip];
}
},
dispatch: (w) => {
let idx, arg, key, val;
switch(byteCodeToName[w.bytecode]) {
case 'LOAD_CONST':
idx = new Buffer([w.code[w.ip++], w.code[w.ip++]]); // Monkeying
arg = w.constPool[idx.readUInt16BE(0)];
w.localStack.push(arg);
break;
case 'ADD':
w.localStack.push(w.localStack.pop() + w.localStack.pop());
break;
case 'PRINT':
w.print(w.localStack.pop());
break;
case 'EQUAL':
w.localStack.push(w.localStack.pop() == w.localStack.pop());
break;
case 'JUMP':
idx = new Buffer([w.code[w.ip++], w.code[w.ip++]]); // Monkeying
w.ip = idx.readUInt16BE(0);
break;
case 'SUB':
let rightOperand = w.localStack.pop();
let leftOperand = w.localStack.pop();
w.localStack.push(leftOperand - rightOperand);
case 'STORE_ENV':
idx = new Buffer([w.code[w.ip++], w.code[w.ip++]]); // Monkeying
key = w.constPool[idx.readUInt16BE(0)];
val = w.localStack.pop();
w.localEnv[key] = val;
break;
case 'LOAD_ENV':
idx = new Buffer([w.code[w.ip++], w.code[w.ip++]]);
key = w.constPool[idx.readUInt16BE(0)];
w.localStack.push(w.localEnv[key]);
break;
case 'CALL':
idx = new Buffer([w.code[w.ip++], w.code[w.ip++]]); // Monkeying
let functionDef = w.constPool[idx.readUInt16BE(0)];
let jumpIndex = functionDef[0]
let functionArgs = functionDef[1]
let argMap = {}
for (let arg of functionArgs){
argMap[w.constPool[arg]] = w.localStack.pop()
}
w.callStack.push({
name: functionDef[2],
returnAddr: w.ip,
localStack: [],
localEnv: argMap
})
w.ip = jumpIndex
break;
case "RETURN":
w.ip = w.callStack.pop().returnAddr
// TODO: add return values
break;
default:
throw "Unknown instruction " + w.bytecode.toString();
}
},
assemble
};
module.exports = vm;