tzo 1.0.18 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +284 -280
- package/bin/package.json +2 -2
- package/bin/tzo +3 -3
- package/bin/tzo.mjs +3 -3
- package/dist/browser/cli.d.ts +1 -1
- package/dist/browser/cli.js +34 -34
- package/dist/browser/cli.js.map +1 -1
- package/dist/browser/concisetext/ConciseTextLexer.d.ts +30 -30
- package/dist/browser/concisetext/ConciseTextLexer.js +122 -122
- package/dist/browser/concisetext/ConciseTextListener.d.ts +14 -14
- package/dist/browser/concisetext/ConciseTextListener.js +35 -35
- package/dist/browser/concisetext/ConciseTextParser.d.ts +99 -99
- package/dist/browser/concisetext/ConciseTextParser.js +438 -438
- package/dist/browser/getStackParams.d.ts +2 -2
- package/dist/browser/getStackParams.js +23 -23
- package/dist/browser/index.d.ts +5 -5
- package/dist/browser/index.js +17 -17
- package/dist/browser/interfaces.d.ts +42 -42
- package/dist/browser/interfaces.js +2 -2
- package/dist/browser/opcodes/and.d.ts +3 -3
- package/dist/browser/opcodes/and.js +8 -8
- package/dist/browser/opcodes/charCode.d.ts +3 -3
- package/dist/browser/opcodes/charCode.js +9 -9
- package/dist/browser/opcodes/closeBrace.d.ts +3 -3
- package/dist/browser/opcodes/closeBrace.js +6 -6
- package/dist/browser/opcodes/closeBrace.js.map +1 -1
- package/dist/browser/opcodes/concat.d.ts +3 -3
- package/dist/browser/opcodes/concat.js +8 -8
- package/dist/browser/opcodes/delContext.d.ts +3 -3
- package/dist/browser/opcodes/delContext.js +8 -8
- package/dist/browser/opcodes/dup.d.ts +6 -6
- package/dist/browser/opcodes/dup.js +12 -12
- package/dist/browser/opcodes/eq.d.ts +3 -3
- package/dist/browser/opcodes/eq.js +9 -9
- package/dist/browser/opcodes/exit.d.ts +3 -3
- package/dist/browser/opcodes/exit.js +6 -6
- package/dist/browser/opcodes/getContext.d.ts +3 -3
- package/dist/browser/opcodes/getContext.js +12 -12
- package/dist/browser/opcodes/goto.d.ts +3 -3
- package/dist/browser/opcodes/goto.js +19 -19
- package/dist/browser/opcodes/gt.d.ts +3 -3
- package/dist/browser/opcodes/gt.js +9 -9
- package/dist/browser/opcodes/hasContext.d.ts +3 -3
- package/dist/browser/opcodes/hasContext.js +13 -13
- package/dist/browser/opcodes/jgz.d.ts +3 -3
- package/dist/browser/opcodes/jgz.js +10 -10
- package/dist/browser/opcodes/jz.d.ts +3 -3
- package/dist/browser/opcodes/jz.js +10 -10
- package/dist/browser/opcodes/lt.d.ts +3 -3
- package/dist/browser/opcodes/lt.js +9 -9
- package/dist/browser/opcodes/min.d.ts +3 -3
- package/dist/browser/opcodes/min.js +10 -10
- package/dist/browser/opcodes/mul.d.ts +3 -3
- package/dist/browser/opcodes/mul.js +10 -10
- package/dist/browser/opcodes/nop.d.ts +6 -6
- package/dist/browser/opcodes/nop.js +9 -9
- package/dist/browser/opcodes/not.d.ts +6 -6
- package/dist/browser/opcodes/not.js +16 -16
- package/dist/browser/opcodes/openBrace.d.ts +3 -3
- package/dist/browser/opcodes/openBrace.js +20 -20
- package/dist/browser/opcodes/openBrace.js.map +1 -1
- package/dist/browser/opcodes/or.d.ts +3 -3
- package/dist/browser/opcodes/or.js +8 -8
- package/dist/browser/opcodes/pause.d.ts +3 -3
- package/dist/browser/opcodes/pause.js +6 -6
- package/dist/browser/opcodes/plus.d.ts +3 -3
- package/dist/browser/opcodes/plus.js +10 -10
- package/dist/browser/opcodes/pop.d.ts +3 -3
- package/dist/browser/opcodes/pop.js +6 -6
- package/dist/browser/opcodes/ppc.d.ts +6 -6
- package/dist/browser/opcodes/ppc.js +9 -9
- package/dist/browser/opcodes/randInt.d.ts +3 -3
- package/dist/browser/opcodes/randInt.js +9 -9
- package/dist/browser/opcodes/rconcat.d.ts +3 -3
- package/dist/browser/opcodes/rconcat.js +8 -8
- package/dist/browser/opcodes/setContext.d.ts +3 -3
- package/dist/browser/opcodes/setContext.js +8 -8
- package/dist/browser/opcodes/stacksize.d.ts +3 -3
- package/dist/browser/opcodes/stacksize.js +6 -6
- package/dist/browser/opcodes/stdout.d.ts +3 -3
- package/dist/browser/opcodes/stdout.js +6 -6
- package/dist/browser/util/builder.d.ts +12 -12
- package/dist/browser/util/builder.js +37 -37
- package/dist/browser/util/fix_antlr_grammar_ts.d.ts +1 -1
- package/dist/browser/util/fix_antlr_grammar_ts.js +7 -7
- package/dist/browser/util/tokenizer.d.ts +20 -20
- package/dist/browser/util/tokenizer.js +176 -176
- package/dist/browser/util/tokenizer.js.map +1 -1
- package/dist/browser/vm.d.ts +26 -26
- package/dist/browser/vm.js +201 -201
- package/dist/browser/vm.test.d.ts +1 -1
- package/dist/browser/vm.test.js +398 -355
- package/dist/browser/vm.test.js.map +1 -1
- package/dist/node/cli.d.ts +1 -1
- package/dist/node/cli.js +34 -34
- package/dist/node/cli.js.map +1 -1
- package/dist/node/concisetext/ConciseTextLexer.d.ts +30 -30
- package/dist/node/concisetext/ConciseTextLexer.js +122 -122
- package/dist/node/concisetext/ConciseTextListener.d.ts +14 -14
- package/dist/node/concisetext/ConciseTextListener.js +35 -35
- package/dist/node/concisetext/ConciseTextParser.d.ts +99 -99
- package/dist/node/concisetext/ConciseTextParser.js +438 -438
- package/dist/node/getStackParams.d.ts +2 -2
- package/dist/node/getStackParams.js +23 -23
- package/dist/node/index.d.ts +5 -5
- package/dist/node/index.js +17 -17
- package/dist/node/interfaces.d.ts +42 -42
- package/dist/node/interfaces.js +2 -2
- package/dist/node/opcodes/and.d.ts +3 -3
- package/dist/node/opcodes/and.js +8 -8
- package/dist/node/opcodes/charCode.d.ts +3 -3
- package/dist/node/opcodes/charCode.js +9 -9
- package/dist/node/opcodes/closeBrace.d.ts +3 -3
- package/dist/node/opcodes/closeBrace.js +6 -6
- package/dist/node/opcodes/closeBrace.js.map +1 -1
- package/dist/node/opcodes/concat.d.ts +3 -3
- package/dist/node/opcodes/concat.js +8 -8
- package/dist/node/opcodes/delContext.d.ts +3 -3
- package/dist/node/opcodes/delContext.js +8 -8
- package/dist/node/opcodes/dup.d.ts +6 -6
- package/dist/node/opcodes/dup.js +12 -12
- package/dist/node/opcodes/eq.d.ts +3 -3
- package/dist/node/opcodes/eq.js +9 -9
- package/dist/node/opcodes/exit.d.ts +3 -3
- package/dist/node/opcodes/exit.js +6 -6
- package/dist/node/opcodes/getContext.d.ts +3 -3
- package/dist/node/opcodes/getContext.js +12 -12
- package/dist/node/opcodes/goto.d.ts +3 -3
- package/dist/node/opcodes/goto.js +19 -19
- package/dist/node/opcodes/gt.d.ts +3 -3
- package/dist/node/opcodes/gt.js +9 -9
- package/dist/node/opcodes/hasContext.d.ts +3 -3
- package/dist/node/opcodes/hasContext.js +13 -13
- package/dist/node/opcodes/jgz.d.ts +3 -3
- package/dist/node/opcodes/jgz.js +10 -10
- package/dist/node/opcodes/jz.d.ts +3 -3
- package/dist/node/opcodes/jz.js +10 -10
- package/dist/node/opcodes/lt.d.ts +3 -3
- package/dist/node/opcodes/lt.js +9 -9
- package/dist/node/opcodes/min.d.ts +3 -3
- package/dist/node/opcodes/min.js +10 -10
- package/dist/node/opcodes/mul.d.ts +3 -3
- package/dist/node/opcodes/mul.js +10 -10
- package/dist/node/opcodes/nop.d.ts +6 -6
- package/dist/node/opcodes/nop.js +9 -9
- package/dist/node/opcodes/not.d.ts +6 -6
- package/dist/node/opcodes/not.js +16 -16
- package/dist/node/opcodes/openBrace.d.ts +3 -3
- package/dist/node/opcodes/openBrace.js +20 -20
- package/dist/node/opcodes/openBrace.js.map +1 -1
- package/dist/node/opcodes/or.d.ts +3 -3
- package/dist/node/opcodes/or.js +8 -8
- package/dist/node/opcodes/pause.d.ts +3 -3
- package/dist/node/opcodes/pause.js +6 -6
- package/dist/node/opcodes/plus.d.ts +3 -3
- package/dist/node/opcodes/plus.js +10 -10
- package/dist/node/opcodes/pop.d.ts +3 -3
- package/dist/node/opcodes/pop.js +6 -6
- package/dist/node/opcodes/ppc.d.ts +6 -6
- package/dist/node/opcodes/ppc.js +9 -9
- package/dist/node/opcodes/randInt.d.ts +3 -3
- package/dist/node/opcodes/randInt.js +9 -9
- package/dist/node/opcodes/rconcat.d.ts +3 -3
- package/dist/node/opcodes/rconcat.js +8 -8
- package/dist/node/opcodes/setContext.d.ts +3 -3
- package/dist/node/opcodes/setContext.js +8 -8
- package/dist/node/opcodes/stacksize.d.ts +3 -3
- package/dist/node/opcodes/stacksize.js +6 -6
- package/dist/node/opcodes/stdout.d.ts +3 -3
- package/dist/node/opcodes/stdout.js +6 -6
- package/dist/node/util/builder.d.ts +12 -12
- package/dist/node/util/builder.js +37 -37
- package/dist/node/util/fix_antlr_grammar_ts.d.ts +1 -1
- package/dist/node/util/fix_antlr_grammar_ts.js +7 -7
- package/dist/node/util/tokenizer.d.ts +20 -20
- package/dist/node/util/tokenizer.js +176 -176
- package/dist/node/util/tokenizer.js.map +1 -1
- package/dist/node/vm.d.ts +26 -26
- package/dist/node/vm.js +201 -201
- package/dist/node/vm.test.d.ts +1 -1
- package/dist/node/vm.test.js +398 -355
- package/dist/node/vm.test.js.map +1 -1
- package/jest.config.js +4 -4
- package/package.json +50 -50
- package/tsconfig-browser.json +8 -8
- package/tsconfig.json +28 -28
package/dist/browser/vm.test.js
CHANGED
|
@@ -1,356 +1,399 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const vm_1 = require("./vm");
|
|
7
|
-
const tokenizer_1 = require("./util/tokenizer");
|
|
8
|
-
const jest_mock_process_1 = require("jest-mock-process");
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const EMIT_TEST_JSONS = true; // set to true to emit test jsons in `src/tests`
|
|
11
|
-
if (EMIT_TEST_JSONS) {
|
|
12
|
-
// clean folder first!
|
|
13
|
-
const files = fs_1.default.readdirSync("./src/tests");
|
|
14
|
-
files.forEach(f => {
|
|
15
|
-
fs_1.default.unlinkSync(`./src/tests/${f}`);
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
function createVMAndRunStdRepCode(instructions, initialContext, initialLabelMap) {
|
|
19
|
-
if (initialLabelMap === undefined) {
|
|
20
|
-
initialLabelMap = {};
|
|
21
|
-
}
|
|
22
|
-
const vm = new vm_1.VM(initialContext !== undefined ? initialContext : {});
|
|
23
|
-
vm.loadProgramList(instructions, initialLabelMap);
|
|
24
|
-
vm.run();
|
|
25
|
-
return vm;
|
|
26
|
-
}
|
|
27
|
-
function createVM(codeBlock, initialContext, initialLabelMap) {
|
|
28
|
-
if (initialLabelMap === undefined) {
|
|
29
|
-
initialLabelMap = {};
|
|
30
|
-
}
|
|
31
|
-
const vm = new vm_1.VM(initialContext !== undefined ? initialContext : {});
|
|
32
|
-
const tokenizer = new tokenizer_1.Tokenizer();
|
|
33
|
-
const instructions = tokenizer.parse(codeBlock);
|
|
34
|
-
vm.loadProgramList(instructions, initialLabelMap);
|
|
35
|
-
return { vm, instructions };
|
|
36
|
-
}
|
|
37
|
-
function createVMAndRunCode(codeBlock, initialContext, initialLabelMap) {
|
|
38
|
-
const { vm } = createVM(codeBlock, initialContext, initialLabelMap);
|
|
39
|
-
vm.run();
|
|
40
|
-
return vm;
|
|
41
|
-
}
|
|
42
|
-
let lastTest = 0;
|
|
43
|
-
function expectVM(codeBlock, initialContext, expectations, testname) {
|
|
44
|
-
const { instructions, vm } = createVM(codeBlock, initialContext);
|
|
45
|
-
let usedTestName = testname;
|
|
46
|
-
if (testname === undefined) {
|
|
47
|
-
usedTestName = "" + lastTest;
|
|
48
|
-
lastTest += 1;
|
|
49
|
-
}
|
|
50
|
-
if (EMIT_TEST_JSONS) {
|
|
51
|
-
fs_1.default.writeFileSync(`./src/tests/${usedTestName}.json`, JSON.stringify({
|
|
52
|
-
input_program: instructions,
|
|
53
|
-
initial_context: initialContext,
|
|
54
|
-
expected: {
|
|
55
|
-
stack: expectations.stack,
|
|
56
|
-
context: expectations.context,
|
|
57
|
-
programCounter: expectations.programCounter
|
|
58
|
-
}
|
|
59
|
-
}, null, 2));
|
|
60
|
-
}
|
|
61
|
-
vm.run();
|
|
62
|
-
if (expectations.stack !== undefined) {
|
|
63
|
-
expect(vm.stack).toEqual(expectations.stack);
|
|
64
|
-
}
|
|
65
|
-
if (expectations.context !== undefined) {
|
|
66
|
-
expect(vm.context).toEqual(expectations.context);
|
|
67
|
-
}
|
|
68
|
-
if (expectations.programCounter !== undefined) {
|
|
69
|
-
expect(vm.programCounter).toEqual(expectations.programCounter);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function expectStack(codeBlock, expectedStack, testname) {
|
|
73
|
-
expectVM(codeBlock, {}, {
|
|
74
|
-
stack: expectedStack
|
|
75
|
-
}, testname);
|
|
76
|
-
}
|
|
77
|
-
test('should have an initial state', () => {
|
|
78
|
-
const vm = new vm_1.VM({});
|
|
79
|
-
expect(vm.programCounter).toBe(0);
|
|
80
|
-
expect(vm.context).toEqual({});
|
|
81
|
-
expect(vm.labelMap).toEqual({});
|
|
82
|
-
expect(vm.programList).toEqual([]);
|
|
83
|
-
expect(vm.stack).toEqual([]);
|
|
84
|
-
expect(vm.exit).toBe(false);
|
|
85
|
-
expect(vm.pause).toBe(false);
|
|
86
|
-
});
|
|
87
|
-
test('should push numbers to the stack', () => {
|
|
88
|
-
expectStack(`1 2 3`, [1, 2, 3], "push_numbers");
|
|
89
|
-
expectStack(`-1 -2 -3`, [-1, -2, -3], "push_negative_numbers");
|
|
90
|
-
});
|
|
91
|
-
test('should push strings to the stack', () => {
|
|
92
|
-
expectStack(`"foo" "bar" "baz"`, ["foo", "bar", "baz"], "push_strings");
|
|
93
|
-
});
|
|
94
|
-
test('should set context', () => {
|
|
95
|
-
expectVM(`"FOO" "bar" setContext`, {}, { context: { bar: "FOO" } }, "setContext_string");
|
|
96
|
-
expectVM(`1 "bar" setContext`, {}, { context: { bar: 1 } }, "setContext_number");
|
|
97
|
-
});
|
|
98
|
-
test('should get context', () => {
|
|
99
|
-
const ctx = { "ham": "ster", "one": 1, "two": 2 };
|
|
100
|
-
expectVM(`"ham" getContext`, ctx, { stack: ["ster"] }, "getContext_1");
|
|
101
|
-
expectVM(`"one" getContext`, ctx, { stack: [1] }, "getContext_2");
|
|
102
|
-
expectVM(`"two" getContext`, ctx, { stack: [2] }, "getContext_3");
|
|
103
|
-
});
|
|
104
|
-
test('should be able to text if context variable exists', () => {
|
|
105
|
-
const ctx = { "ham": "ster", "one": 1, "two": 2 };
|
|
106
|
-
expectVM(`"ham" hasContext`, ctx, { stack: [1] }, "hasContext_1");
|
|
107
|
-
expectVM(`"one" hasContext`, ctx, { stack: [1] }, "hasContext_2");
|
|
108
|
-
expectVM(`"foobar" hasContext`, ctx, { stack: [0] }, "hasContext_3");
|
|
109
|
-
});
|
|
110
|
-
test('should delete context', () => {
|
|
111
|
-
expectVM(`"FOO" "bar" setContext "bar" delContext`, {}, { context: {} }, "delContext");
|
|
112
|
-
});
|
|
113
|
-
test('should throw an error when trying to get context for unknown variables', () => {
|
|
114
|
-
const ctx = { "ham": "ster", "one": 1, "two": 2 };
|
|
115
|
-
expect(() => createVMAndRunCode(`"asdfzxc" getContext`, ctx)).toThrow();
|
|
116
|
-
});
|
|
117
|
-
test('should
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
expectStack(`
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
expectStack(`
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
expectStack(`0
|
|
141
|
-
expectStack(`1
|
|
142
|
-
expectStack(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
expectStack(
|
|
147
|
-
expectStack(`
|
|
148
|
-
});
|
|
149
|
-
test('should
|
|
150
|
-
expectStack(`1 2
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
expectStack(`
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
expectStack(`
|
|
164
|
-
expectStack(`
|
|
165
|
-
expectStack(`
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
expectStack(`
|
|
173
|
-
expectStack(`1
|
|
174
|
-
expectStack(`-1
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
expectStack(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
expectStack(`1
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
expectStack(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
expectStack(`
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
expectStack(`"
|
|
197
|
-
expectStack(`
|
|
198
|
-
expectStack(`1
|
|
199
|
-
expectStack(`"
|
|
200
|
-
});
|
|
201
|
-
test('should
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
expectStack(`
|
|
231
|
-
|
|
232
|
-
});
|
|
233
|
-
test('should
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
expectStack(`
|
|
259
|
-
expectStack(`
|
|
260
|
-
});
|
|
261
|
-
test('should
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
expectStack(`
|
|
268
|
-
expectStack(`
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
expect(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
{
|
|
305
|
-
"type": "
|
|
306
|
-
"
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
"type": "push-number-instruction",
|
|
310
|
-
"value":
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
"
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
});
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vm_1 = require("./vm");
|
|
7
|
+
const tokenizer_1 = require("./util/tokenizer");
|
|
8
|
+
const jest_mock_process_1 = require("jest-mock-process");
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const EMIT_TEST_JSONS = true; // set to true to emit test jsons in `src/tests`
|
|
11
|
+
if (EMIT_TEST_JSONS) {
|
|
12
|
+
// clean folder first!
|
|
13
|
+
const files = fs_1.default.readdirSync("./src/tests");
|
|
14
|
+
files.forEach(f => {
|
|
15
|
+
fs_1.default.unlinkSync(`./src/tests/${f}`);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function createVMAndRunStdRepCode(instructions, initialContext, initialLabelMap) {
|
|
19
|
+
if (initialLabelMap === undefined) {
|
|
20
|
+
initialLabelMap = {};
|
|
21
|
+
}
|
|
22
|
+
const vm = new vm_1.VM(initialContext !== undefined ? initialContext : {});
|
|
23
|
+
vm.loadProgramList(instructions, initialLabelMap);
|
|
24
|
+
vm.run();
|
|
25
|
+
return vm;
|
|
26
|
+
}
|
|
27
|
+
function createVM(codeBlock, initialContext, initialLabelMap) {
|
|
28
|
+
if (initialLabelMap === undefined) {
|
|
29
|
+
initialLabelMap = {};
|
|
30
|
+
}
|
|
31
|
+
const vm = new vm_1.VM(initialContext !== undefined ? initialContext : {});
|
|
32
|
+
const tokenizer = new tokenizer_1.Tokenizer();
|
|
33
|
+
const instructions = tokenizer.parse(codeBlock);
|
|
34
|
+
vm.loadProgramList(instructions, initialLabelMap);
|
|
35
|
+
return { vm, instructions };
|
|
36
|
+
}
|
|
37
|
+
function createVMAndRunCode(codeBlock, initialContext, initialLabelMap) {
|
|
38
|
+
const { vm } = createVM(codeBlock, initialContext, initialLabelMap);
|
|
39
|
+
vm.run();
|
|
40
|
+
return vm;
|
|
41
|
+
}
|
|
42
|
+
let lastTest = 0;
|
|
43
|
+
function expectVM(codeBlock, initialContext, expectations, testname) {
|
|
44
|
+
const { instructions, vm } = createVM(codeBlock, initialContext);
|
|
45
|
+
let usedTestName = testname;
|
|
46
|
+
if (testname === undefined) {
|
|
47
|
+
usedTestName = "" + lastTest;
|
|
48
|
+
lastTest += 1;
|
|
49
|
+
}
|
|
50
|
+
if (EMIT_TEST_JSONS) {
|
|
51
|
+
fs_1.default.writeFileSync(`./src/tests/${usedTestName}.json`, JSON.stringify({
|
|
52
|
+
input_program: instructions,
|
|
53
|
+
initial_context: initialContext,
|
|
54
|
+
expected: {
|
|
55
|
+
stack: expectations.stack,
|
|
56
|
+
context: expectations.context,
|
|
57
|
+
programCounter: expectations.programCounter
|
|
58
|
+
}
|
|
59
|
+
}, null, 2));
|
|
60
|
+
}
|
|
61
|
+
vm.run();
|
|
62
|
+
if (expectations.stack !== undefined) {
|
|
63
|
+
expect(vm.stack).toEqual(expectations.stack);
|
|
64
|
+
}
|
|
65
|
+
if (expectations.context !== undefined) {
|
|
66
|
+
expect(vm.context).toEqual(expectations.context);
|
|
67
|
+
}
|
|
68
|
+
if (expectations.programCounter !== undefined) {
|
|
69
|
+
expect(vm.programCounter).toEqual(expectations.programCounter);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function expectStack(codeBlock, expectedStack, testname) {
|
|
73
|
+
expectVM(codeBlock, {}, {
|
|
74
|
+
stack: expectedStack
|
|
75
|
+
}, testname);
|
|
76
|
+
}
|
|
77
|
+
test('should have an initial state', () => {
|
|
78
|
+
const vm = new vm_1.VM({});
|
|
79
|
+
expect(vm.programCounter).toBe(0);
|
|
80
|
+
expect(vm.context).toEqual({});
|
|
81
|
+
expect(vm.labelMap).toEqual({});
|
|
82
|
+
expect(vm.programList).toEqual([]);
|
|
83
|
+
expect(vm.stack).toEqual([]);
|
|
84
|
+
expect(vm.exit).toBe(false);
|
|
85
|
+
expect(vm.pause).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
test('should push numbers to the stack', () => {
|
|
88
|
+
expectStack(`1 2 3`, [1, 2, 3], "push_numbers");
|
|
89
|
+
expectStack(`-1 -2 -3`, [-1, -2, -3], "push_negative_numbers");
|
|
90
|
+
});
|
|
91
|
+
test('should push strings to the stack', () => {
|
|
92
|
+
expectStack(`"foo" "bar" "baz"`, ["foo", "bar", "baz"], "push_strings");
|
|
93
|
+
});
|
|
94
|
+
test('should set context', () => {
|
|
95
|
+
expectVM(`"FOO" "bar" setContext`, {}, { context: { bar: "FOO" } }, "setContext_string");
|
|
96
|
+
expectVM(`1 "bar" setContext`, {}, { context: { bar: 1 } }, "setContext_number");
|
|
97
|
+
});
|
|
98
|
+
test('should get context', () => {
|
|
99
|
+
const ctx = { "ham": "ster", "one": 1, "two": 2 };
|
|
100
|
+
expectVM(`"ham" getContext`, ctx, { stack: ["ster"] }, "getContext_1");
|
|
101
|
+
expectVM(`"one" getContext`, ctx, { stack: [1] }, "getContext_2");
|
|
102
|
+
expectVM(`"two" getContext`, ctx, { stack: [2] }, "getContext_3");
|
|
103
|
+
});
|
|
104
|
+
test('should be able to text if context variable exists', () => {
|
|
105
|
+
const ctx = { "ham": "ster", "one": 1, "two": 2 };
|
|
106
|
+
expectVM(`"ham" hasContext`, ctx, { stack: [1] }, "hasContext_1");
|
|
107
|
+
expectVM(`"one" hasContext`, ctx, { stack: [1] }, "hasContext_2");
|
|
108
|
+
expectVM(`"foobar" hasContext`, ctx, { stack: [0] }, "hasContext_3");
|
|
109
|
+
});
|
|
110
|
+
test('should delete context', () => {
|
|
111
|
+
expectVM(`"FOO" "bar" setContext "bar" delContext`, {}, { context: {} }, "delContext");
|
|
112
|
+
});
|
|
113
|
+
test('should throw an error when trying to get context for unknown variables', () => {
|
|
114
|
+
const ctx = { "ham": "ster", "one": 1, "two": 2 };
|
|
115
|
+
expect(() => createVMAndRunCode(`"asdfzxc" getContext`, ctx)).toThrow();
|
|
116
|
+
});
|
|
117
|
+
test('should throw an error when using a non-string key for context operations', () => {
|
|
118
|
+
const ctx = { "ham": "ster", "one": 1, "two": 2 };
|
|
119
|
+
// the key is the top of the stack, so a numeric key means the number is last pushed
|
|
120
|
+
expect(() => createVMAndRunCode(`"value" 5 setContext`, ctx)).toThrow();
|
|
121
|
+
expect(() => createVMAndRunCode(`5 getContext`, ctx)).toThrow();
|
|
122
|
+
expect(() => createVMAndRunCode(`5 hasContext`, ctx)).toThrow();
|
|
123
|
+
expect(() => createVMAndRunCode(`5 delContext`, ctx)).toThrow();
|
|
124
|
+
});
|
|
125
|
+
test('should throw an error when adding/subtracting/multiplying non-numbers', () => {
|
|
126
|
+
expect(() => createVMAndRunCode(`"foo" 1 +`, {})).toThrow();
|
|
127
|
+
expect(() => createVMAndRunCode(`1 "foo" +`, {})).toThrow();
|
|
128
|
+
expect(() => createVMAndRunCode(`"foo" 1 -`, {})).toThrow();
|
|
129
|
+
expect(() => createVMAndRunCode(`"foo" 1 *`, {})).toThrow();
|
|
130
|
+
});
|
|
131
|
+
test('randInt with max 0 should push 0 without throwing', () => {
|
|
132
|
+
expectStack(`0 randInt`, [0], "randint_zero");
|
|
133
|
+
});
|
|
134
|
+
test('charCode should map full code points like String.fromCharCode', () => {
|
|
135
|
+
expectStack(`97 charCode`, ["a"], "charcode_ascii");
|
|
136
|
+
expectStack(`256 charCode`, ["Ā"], "charcode_codepoint");
|
|
137
|
+
});
|
|
138
|
+
test('comparison and jump ops should use float semantics, not integer truncation', () => {
|
|
139
|
+
// concise syntax now parses floats too, so these use decimal literals directly
|
|
140
|
+
expectStack(`0.1 0.9 gt`, [1], "gt_fractional");
|
|
141
|
+
expectStack(`0.9 0.1 lt`, [1], "lt_fractional");
|
|
142
|
+
expectStack(`0.5 jgz 99`, [], "jgz_fractional_skip");
|
|
143
|
+
expectStack(`0.5 jz 99`, [99], "jz_fractional_no_skip");
|
|
144
|
+
});
|
|
145
|
+
test('concise syntax should parse decimal number literals', () => {
|
|
146
|
+
expectStack(`1.5 2.5 +`, [4], "decimal_parse_add");
|
|
147
|
+
expectStack(`0.5 0.5 +`, [1], "decimal_parse_add2");
|
|
148
|
+
});
|
|
149
|
+
test('should add numbers', () => {
|
|
150
|
+
expectStack(`1 2 +`, [3], "plus_0");
|
|
151
|
+
expectStack(`1 2 plus`, [3], "plus_1");
|
|
152
|
+
});
|
|
153
|
+
test('should subtract numbers', () => {
|
|
154
|
+
expectStack(`1 2 -`, [1], "min_0");
|
|
155
|
+
expectStack(`1 2 min`, [1], "min_1");
|
|
156
|
+
expectStack(`1 10 min`, [9], "min_2");
|
|
157
|
+
});
|
|
158
|
+
test('should multiply numbers', () => {
|
|
159
|
+
expectStack(`2 3 *`, [6], "multiply_0");
|
|
160
|
+
expectStack(`2 3 mul`, [6], "multiply_1");
|
|
161
|
+
});
|
|
162
|
+
test('should support the not instruction', () => {
|
|
163
|
+
expectStack(`0 not`, [1], "not_0");
|
|
164
|
+
expectStack(`1 not`, [0], "not_1");
|
|
165
|
+
expectStack(`42 not`, [0], "not_2");
|
|
166
|
+
});
|
|
167
|
+
test('should support jgz, { and } for jgz-style if statements', () => {
|
|
168
|
+
expectStack(`1 jgz { "EXPECT_THIS" } "AND_THIS"`, ["EXPECT_THIS", "AND_THIS"], "if_jgz_0");
|
|
169
|
+
expectStack(`0 jgz { "EXPECT_NOT_THIS" } "EXPECT_ONLY_THIS"`, ["EXPECT_ONLY_THIS"], "if_jgz_1");
|
|
170
|
+
});
|
|
171
|
+
test('should support jz', () => {
|
|
172
|
+
expectStack(`0 jz "no"`, [], "jz_0");
|
|
173
|
+
expectStack(`1 jz "yes"`, ["yes"], "jz_1");
|
|
174
|
+
expectStack(`-1 jz "yes"`, ["yes"], "jz_2");
|
|
175
|
+
});
|
|
176
|
+
test('should support jgz', () => {
|
|
177
|
+
expectStack(`0 jgz "yes"`, ["yes"], "jgz_0");
|
|
178
|
+
expectStack(`-1 jgz "yes"`, ["yes"], "jgz_1");
|
|
179
|
+
expectStack(`1 jgz "no"`, [], "jgz_2");
|
|
180
|
+
});
|
|
181
|
+
test('should support braces', () => {
|
|
182
|
+
expectStack(`1 2 { 3 4 5 } 6 7`, [1, 2, 6, 7], "braces");
|
|
183
|
+
});
|
|
184
|
+
test('should support nested braces', () => {
|
|
185
|
+
expectStack(`1 2 { 3 { 4 5 } 6 } 7`, [1, 2, 7], "braces_nested");
|
|
186
|
+
});
|
|
187
|
+
test('should support the goto instruction', () => {
|
|
188
|
+
expectStack(`5 goto "foo" "bar" "baz" "quux" exit`, ["quux"], "goto_by_number");
|
|
189
|
+
expectStack(`"myLabel" goto "foo" "bar" "baz" "quux" #myLabel exit`, ["quux"], "goto_by_label");
|
|
190
|
+
expect(createVMAndRunCode(`"myLabel" goto "foo" "bar" "baz" "quux" exit`, {}, { myLabel: 5 }).stack).toEqual(["quux"]);
|
|
191
|
+
});
|
|
192
|
+
test('should calculate equality between values on the stack', () => {
|
|
193
|
+
expectStack(`1 1 eq`, [1], "eq_0"); // equal
|
|
194
|
+
expectStack(`0 0 eq`, [1], "eq_1"); // equal
|
|
195
|
+
expectStack(`"foo" "foo" eq`, [1], "eq_2"); // equal
|
|
196
|
+
expectStack(`"foo" "bar" eq`, [0], "eq_3"); // not equal
|
|
197
|
+
expectStack(`1 2 eq`, [0], "eq_4"); // not equal
|
|
198
|
+
expectStack(`2 1 eq`, [0], "eq_5"); // not equal
|
|
199
|
+
expectStack(`"0" 0 eq`, [0], "eq_6"); // not equal
|
|
200
|
+
});
|
|
201
|
+
test('should calculate greater than', () => {
|
|
202
|
+
expectStack(`1 1 gt`, [0], 'gt_0'); // not greater than
|
|
203
|
+
expectStack(`1 0 gt`, [0], 'gt_1'); // not greater than
|
|
204
|
+
expectStack(`1 2 gt`, [1], 'gt_2'); // greater than
|
|
205
|
+
expectStack(`1 -2 gt`, [0], 'gt_3'); // not greater than
|
|
206
|
+
expectStack(`-1 2 gt`, [1], 'gt_4'); // greater than
|
|
207
|
+
expectStack(`-1 -2 gt`, [0], 'gt_5'); // not greater than
|
|
208
|
+
expectStack(`-2 -1 gt`, [1], 'gt_6'); // greater than
|
|
209
|
+
expectStack(`-2 -2 gt`, [0], 'gt_7'); // not greater than
|
|
210
|
+
});
|
|
211
|
+
test('should calculate less than', () => {
|
|
212
|
+
expectStack(`1 1 lt`, [0], 'lt_0'); // not less than
|
|
213
|
+
expectStack(`1 2 lt`, [0], 'lt_1'); // not less than
|
|
214
|
+
expectStack(`1 0 lt`, [1], 'lt_2'); // less than
|
|
215
|
+
expectStack(`1 -1 lt`, [1], 'lt_3'); // less than
|
|
216
|
+
expectStack(`-1 -1 lt`, [0], 'lt_4'); // not less than
|
|
217
|
+
expectStack(`-1 -2 lt`, [1], 'lt_5'); // not less than
|
|
218
|
+
expectStack(`-1 2 lt`, [0], 'lt_6'); // not less than
|
|
219
|
+
expectStack(`2 -1 lt`, [1], 'lt_7'); // not less than
|
|
220
|
+
});
|
|
221
|
+
test('should concat', () => {
|
|
222
|
+
expectStack(`"one" "two" concat`, ["twoone"], "concat_0");
|
|
223
|
+
expectStack(`"1" "two" concat`, ["two1"], "concat_1");
|
|
224
|
+
expectStack(`1 2 concat`, ["21"], "concat_2");
|
|
225
|
+
expectStack(`"one" 2 concat`, ["2one"], "concat_3");
|
|
226
|
+
});
|
|
227
|
+
test('should rconcat (reverse concat)', () => {
|
|
228
|
+
expectStack(`"one" "two" rconcat`, ["onetwo"], "rconcat_0");
|
|
229
|
+
expectStack(`"1" "two" rconcat`, ["1two"], "rconcat_1");
|
|
230
|
+
expectStack(`1 2 rconcat`, ["12"], "rconcat_2");
|
|
231
|
+
expectStack(`"one" 2 rconcat`, ["one2"], "rconcat_3");
|
|
232
|
+
});
|
|
233
|
+
test('should pause', () => {
|
|
234
|
+
const vm = createVMAndRunCode(`"foo" "bar" pause "baz" "quux"`);
|
|
235
|
+
expect(vm.stack).toEqual(["foo", "bar"]);
|
|
236
|
+
expect(vm.pause).toEqual(true);
|
|
237
|
+
expect(vm.exit).toEqual(false);
|
|
238
|
+
expect(vm.programCounter).toEqual(3); // pause WILL increment the programCounter!
|
|
239
|
+
vm.run();
|
|
240
|
+
expect(vm.stack).toEqual(["foo", "bar", "baz", "quux"]);
|
|
241
|
+
expect(vm.pause).toEqual(false);
|
|
242
|
+
expect(vm.exit).toEqual(true);
|
|
243
|
+
expect(vm.programCounter).toEqual(5);
|
|
244
|
+
});
|
|
245
|
+
test('should exit', () => {
|
|
246
|
+
const vm = createVMAndRunCode(`"foo" "bar" exit "baz" "quux"`);
|
|
247
|
+
expect(vm.stack).toEqual(["foo", "bar"]);
|
|
248
|
+
expect(vm.pause).toEqual(false);
|
|
249
|
+
expect(vm.exit).toEqual(true);
|
|
250
|
+
expect(vm.programCounter).toEqual(3); // exit WILL increment the programCounter!
|
|
251
|
+
vm.run(); // this is a no-op, effectively, as the VM can not run anymore!
|
|
252
|
+
expect(vm.stack).toEqual(["foo", "bar"]);
|
|
253
|
+
expect(vm.pause).toEqual(false);
|
|
254
|
+
expect(vm.exit).toEqual(true);
|
|
255
|
+
expect(vm.programCounter).toEqual(3);
|
|
256
|
+
});
|
|
257
|
+
test('should push program counter on ppc instruction', () => {
|
|
258
|
+
expectStack(`nop nop ppc`, [2], "ppc_0");
|
|
259
|
+
expectStack(`ppc`, [0], "ppc_1");
|
|
260
|
+
});
|
|
261
|
+
test('should do nothing on nop instruction', () => {
|
|
262
|
+
expectStack(`nop nop nop`, [], "nop_0");
|
|
263
|
+
expectVM(`nop`, {}, { programCounter: 1, context: {}, stack: [] }, "nop_1");
|
|
264
|
+
});
|
|
265
|
+
test('should calculate and', () => {
|
|
266
|
+
expectStack(`1 1 and`, [1], "and_0");
|
|
267
|
+
expectStack(`1 0 and`, [0], "and_1");
|
|
268
|
+
expectStack(`0 1 and`, [0], "and_2");
|
|
269
|
+
expectStack(`0 0 and`, [0], "and_3");
|
|
270
|
+
expectStack(`1 9 and`, [1], "and_4");
|
|
271
|
+
expectStack(`9 1 and`, [1], "and_5");
|
|
272
|
+
});
|
|
273
|
+
test('should calculate or', () => {
|
|
274
|
+
expectStack(`1 1 or`, [1], "or_0");
|
|
275
|
+
expectStack(`1 0 or`, [1], "or_1");
|
|
276
|
+
expectStack(`0 1 or`, [1], "or_2");
|
|
277
|
+
expectStack(`0 0 or`, [0], "or_3");
|
|
278
|
+
expectStack(`1 9 or`, [1], "or_4");
|
|
279
|
+
expectStack(`9 1 or`, [1], "or_5");
|
|
280
|
+
});
|
|
281
|
+
test('should duplicate top item from stack via dup', () => {
|
|
282
|
+
expectStack(`1 dup`, [1, 1], "dup_0");
|
|
283
|
+
expectStack(`0 dup`, [0, 0], "dup_1");
|
|
284
|
+
expectStack(`"test" dup`, ["test", "test"], "dup_2");
|
|
285
|
+
expectStack(`42 dup`, [42, 42], "dup_3");
|
|
286
|
+
});
|
|
287
|
+
test('should pop top item from stack via pop', () => {
|
|
288
|
+
expectStack(`1 pop`, [], "pop_0");
|
|
289
|
+
expectStack(`0 pop`, [], "pop_1");
|
|
290
|
+
expectStack(`"test" pop`, [], "pop_2");
|
|
291
|
+
expectStack(`1 2 3 pop`, [1, 2], "pop_3");
|
|
292
|
+
});
|
|
293
|
+
test('should support labels via tokenizer', () => {
|
|
294
|
+
const vm = createVMAndRunCode(`1 "Awesome" goto 2 3 #Awesome nop`);
|
|
295
|
+
expect(vm.labelMap["Awesome"]).toBe(4);
|
|
296
|
+
expect(vm.stack).toEqual([1, 3]);
|
|
297
|
+
});
|
|
298
|
+
test('should support the`stacksize opcode', () => {
|
|
299
|
+
expectStack(`"f" "o" "o" stacksize`, ["f", "o", "o", 3], "stacksize_0");
|
|
300
|
+
expectStack(`stacksize`, [0], "stacksize_1");
|
|
301
|
+
});
|
|
302
|
+
test('should support Standard Representation code', () => {
|
|
303
|
+
expect(createVMAndRunStdRepCode([
|
|
304
|
+
{
|
|
305
|
+
"type": "push-number-instruction",
|
|
306
|
+
"value": 42
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"type": "push-number-instruction",
|
|
310
|
+
"value": 8
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"type": "invoke-function-instruction",
|
|
314
|
+
"functionName": "+"
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"type": "push-string-instruction",
|
|
318
|
+
"value": "Foo"
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"type": "invoke-function-instruction",
|
|
322
|
+
"functionName": "setContext"
|
|
323
|
+
}
|
|
324
|
+
]).context).toEqual({ "Foo": 50 });
|
|
325
|
+
});
|
|
326
|
+
test('should support labels in Standard Representation code', () => {
|
|
327
|
+
expect(createVMAndRunStdRepCode([
|
|
328
|
+
{
|
|
329
|
+
"type": "push-number-instruction",
|
|
330
|
+
"value": 1
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"type": "push-string-instruction",
|
|
334
|
+
"value": "Awesome"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"type": "invoke-function-instruction",
|
|
338
|
+
"functionName": "goto"
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"type": "push-number-instruction",
|
|
342
|
+
"value": 2,
|
|
343
|
+
"comment": "Due to the goto above, this instruction will be skipped, and so 2 won't be pushed to the stack"
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"type": "push-number-instruction",
|
|
347
|
+
"value": 3,
|
|
348
|
+
"label": "Awesome"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"type": "invoke-function-instruction",
|
|
352
|
+
"functionName": "nop"
|
|
353
|
+
}
|
|
354
|
+
]).stack).toEqual([1, 3]);
|
|
355
|
+
});
|
|
356
|
+
test('example tests', () => {
|
|
357
|
+
expectStack(`1 1 + 2 eq jgz { "1 + 1 = 2!" }`, ["1 + 1 = 2!"], "example_0");
|
|
358
|
+
expectStack(`1 1 + 2 eq dup jgz { "1 + 1 = 2!" pop } jz { "1 + 1 is not 2!?" }`, [], "example_1");
|
|
359
|
+
expectStack(`1 1 + 2 eq "conditionResult" setContext "conditionResult" getContext jgz { "1 + 1 = 2!" } "conditionResult" getContext jz { "1 + 1 is not 2!?" }`, ["1 + 1 = 2!"], "example_2");
|
|
360
|
+
expectStack(`"Hello" "," " world!" rconcat rconcat`, ["Hello, world!"], "example_3");
|
|
361
|
+
expectStack(`1 2 3 4 "hi" 0 -1 0 0 1 1 stacksize jgz { pop } stacksize jgz { 9 ppc - goto }`, [], "example_4");
|
|
362
|
+
});
|
|
363
|
+
test('poor man\'s function', () => {
|
|
364
|
+
expectVM(`
|
|
365
|
+
{
|
|
366
|
+
nop #mul3
|
|
367
|
+
"_mul3_return_pc" setContext
|
|
368
|
+
3 *
|
|
369
|
+
"_mul3_return_pc" getContext 3 + "_mul3_return_pc" delContext goto
|
|
370
|
+
}
|
|
371
|
+
1 ppc "mul3" goto
|
|
372
|
+
2 ppc "mul3" goto
|
|
373
|
+
3 ppc "mul3" goto
|
|
374
|
+
4 ppc "mul3" goto
|
|
375
|
+
`, {}, {
|
|
376
|
+
stack: [3, 6, 9, 12],
|
|
377
|
+
context: {}
|
|
378
|
+
}, 'poor_mans_function');
|
|
379
|
+
});
|
|
380
|
+
test('stdout', () => {
|
|
381
|
+
const stdOut = jest_mock_process_1.mockProcessStdout();
|
|
382
|
+
expectStack(`1 stdout`, [], "stdout_0");
|
|
383
|
+
expect(stdOut).toHaveBeenCalledWith('1');
|
|
384
|
+
expectStack(`"hello" stdout`, [], "stdout_1");
|
|
385
|
+
expect(stdOut).toHaveBeenCalledWith('hello');
|
|
386
|
+
stdOut.mockRestore();
|
|
387
|
+
});
|
|
388
|
+
test('stdout should coerce values like JavaScript (negative zero and empty stack)', () => {
|
|
389
|
+
const stdOut = jest_mock_process_1.mockProcessStdout();
|
|
390
|
+
// -1 0 * evaluates to -0, and "" + -0 === "0" in JavaScript
|
|
391
|
+
createVMAndRunCode(`-1 0 * stdout`);
|
|
392
|
+
expect(stdOut).toHaveBeenCalledWith('0');
|
|
393
|
+
stdOut.mockClear();
|
|
394
|
+
// popping from an empty stack yields undefined, and "" + undefined === "undefined"
|
|
395
|
+
createVMAndRunCode(`stdout`);
|
|
396
|
+
expect(stdOut).toHaveBeenCalledWith('undefined');
|
|
397
|
+
stdOut.mockRestore();
|
|
398
|
+
});
|
|
356
399
|
//# sourceMappingURL=vm.test.js.map
|