-
Notifications
You must be signed in to change notification settings - Fork 0
/
stack-vm.ts
82 lines (65 loc) · 1.63 KB
/
stack-vm.ts
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
export enum Op {
ASSIGN,
CALL,
GET,
JUMP,
LABEL,
PRINT,
PUSH,
RETURN
}
export type Program = Instruction[];
export type Instruction = [Op, any] | [any];
export type Stack = any[];
export type Operation = (vm: StackVM, value?: any) => void;
export type Frame = [number, Dict<any>];
export interface Operations {
[key: number]: Operation;
}
export interface Dict<T> {
[key: string]: T;
}
export interface Generator {
next: (...args: any[]) => any;
done: boolean;
}
export default class StackVM {
callStack: Frame[] = [];
labels: Dict<number> = {};
operations: Operations = {};
pc: number = 0;
program: Program;
stack: Stack = [];
variables: Dict<any> = {};
addOperation(opcode: Op, operation: Operation): void {
this.operations[opcode] = operation;
}
load(program: string): void {
let decoded = JSON.parse(program);
this.scan(decoded);
this.program = decoded;
}
run(): IterableIterator<Instruction> {
function* processInstruction(vm: StackVM): IterableIterator<Instruction> {
while (vm.pc < vm.program.length) {
let instruction: Instruction = vm.program[vm.pc];
let op: Op = instruction[0];
let value: any = instruction[1];
vm.operations[op](vm, value);
vm.pc++;
yield instruction;
}
}
return processInstruction(this);
}
private scan(program: Program): void {
this.labels = program.reduce((memo, instruction, pc) => {
let op: Op = instruction[0];
let value: any = instruction[1];
if (op === Op.LABEL) {
memo[value] = pc;
}
return memo;
}, {} as Dict<number>);
}
}