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.
Files changed (186) hide show
  1. package/README.md +284 -280
  2. package/bin/package.json +2 -2
  3. package/bin/tzo +3 -3
  4. package/bin/tzo.mjs +3 -3
  5. package/dist/browser/cli.d.ts +1 -1
  6. package/dist/browser/cli.js +34 -34
  7. package/dist/browser/cli.js.map +1 -1
  8. package/dist/browser/concisetext/ConciseTextLexer.d.ts +30 -30
  9. package/dist/browser/concisetext/ConciseTextLexer.js +122 -122
  10. package/dist/browser/concisetext/ConciseTextListener.d.ts +14 -14
  11. package/dist/browser/concisetext/ConciseTextListener.js +35 -35
  12. package/dist/browser/concisetext/ConciseTextParser.d.ts +99 -99
  13. package/dist/browser/concisetext/ConciseTextParser.js +438 -438
  14. package/dist/browser/getStackParams.d.ts +2 -2
  15. package/dist/browser/getStackParams.js +23 -23
  16. package/dist/browser/index.d.ts +5 -5
  17. package/dist/browser/index.js +17 -17
  18. package/dist/browser/interfaces.d.ts +42 -42
  19. package/dist/browser/interfaces.js +2 -2
  20. package/dist/browser/opcodes/and.d.ts +3 -3
  21. package/dist/browser/opcodes/and.js +8 -8
  22. package/dist/browser/opcodes/charCode.d.ts +3 -3
  23. package/dist/browser/opcodes/charCode.js +9 -9
  24. package/dist/browser/opcodes/closeBrace.d.ts +3 -3
  25. package/dist/browser/opcodes/closeBrace.js +6 -6
  26. package/dist/browser/opcodes/closeBrace.js.map +1 -1
  27. package/dist/browser/opcodes/concat.d.ts +3 -3
  28. package/dist/browser/opcodes/concat.js +8 -8
  29. package/dist/browser/opcodes/delContext.d.ts +3 -3
  30. package/dist/browser/opcodes/delContext.js +8 -8
  31. package/dist/browser/opcodes/dup.d.ts +6 -6
  32. package/dist/browser/opcodes/dup.js +12 -12
  33. package/dist/browser/opcodes/eq.d.ts +3 -3
  34. package/dist/browser/opcodes/eq.js +9 -9
  35. package/dist/browser/opcodes/exit.d.ts +3 -3
  36. package/dist/browser/opcodes/exit.js +6 -6
  37. package/dist/browser/opcodes/getContext.d.ts +3 -3
  38. package/dist/browser/opcodes/getContext.js +12 -12
  39. package/dist/browser/opcodes/goto.d.ts +3 -3
  40. package/dist/browser/opcodes/goto.js +19 -19
  41. package/dist/browser/opcodes/gt.d.ts +3 -3
  42. package/dist/browser/opcodes/gt.js +9 -9
  43. package/dist/browser/opcodes/hasContext.d.ts +3 -3
  44. package/dist/browser/opcodes/hasContext.js +13 -13
  45. package/dist/browser/opcodes/jgz.d.ts +3 -3
  46. package/dist/browser/opcodes/jgz.js +10 -10
  47. package/dist/browser/opcodes/jz.d.ts +3 -3
  48. package/dist/browser/opcodes/jz.js +10 -10
  49. package/dist/browser/opcodes/lt.d.ts +3 -3
  50. package/dist/browser/opcodes/lt.js +9 -9
  51. package/dist/browser/opcodes/min.d.ts +3 -3
  52. package/dist/browser/opcodes/min.js +10 -10
  53. package/dist/browser/opcodes/mul.d.ts +3 -3
  54. package/dist/browser/opcodes/mul.js +10 -10
  55. package/dist/browser/opcodes/nop.d.ts +6 -6
  56. package/dist/browser/opcodes/nop.js +9 -9
  57. package/dist/browser/opcodes/not.d.ts +6 -6
  58. package/dist/browser/opcodes/not.js +16 -16
  59. package/dist/browser/opcodes/openBrace.d.ts +3 -3
  60. package/dist/browser/opcodes/openBrace.js +20 -20
  61. package/dist/browser/opcodes/openBrace.js.map +1 -1
  62. package/dist/browser/opcodes/or.d.ts +3 -3
  63. package/dist/browser/opcodes/or.js +8 -8
  64. package/dist/browser/opcodes/pause.d.ts +3 -3
  65. package/dist/browser/opcodes/pause.js +6 -6
  66. package/dist/browser/opcodes/plus.d.ts +3 -3
  67. package/dist/browser/opcodes/plus.js +10 -10
  68. package/dist/browser/opcodes/pop.d.ts +3 -3
  69. package/dist/browser/opcodes/pop.js +6 -6
  70. package/dist/browser/opcodes/ppc.d.ts +6 -6
  71. package/dist/browser/opcodes/ppc.js +9 -9
  72. package/dist/browser/opcodes/randInt.d.ts +3 -3
  73. package/dist/browser/opcodes/randInt.js +9 -9
  74. package/dist/browser/opcodes/rconcat.d.ts +3 -3
  75. package/dist/browser/opcodes/rconcat.js +8 -8
  76. package/dist/browser/opcodes/setContext.d.ts +3 -3
  77. package/dist/browser/opcodes/setContext.js +8 -8
  78. package/dist/browser/opcodes/stacksize.d.ts +3 -3
  79. package/dist/browser/opcodes/stacksize.js +6 -6
  80. package/dist/browser/opcodes/stdout.d.ts +3 -3
  81. package/dist/browser/opcodes/stdout.js +6 -6
  82. package/dist/browser/util/builder.d.ts +12 -12
  83. package/dist/browser/util/builder.js +37 -37
  84. package/dist/browser/util/fix_antlr_grammar_ts.d.ts +1 -1
  85. package/dist/browser/util/fix_antlr_grammar_ts.js +7 -7
  86. package/dist/browser/util/tokenizer.d.ts +20 -20
  87. package/dist/browser/util/tokenizer.js +176 -176
  88. package/dist/browser/util/tokenizer.js.map +1 -1
  89. package/dist/browser/vm.d.ts +26 -26
  90. package/dist/browser/vm.js +201 -201
  91. package/dist/browser/vm.test.d.ts +1 -1
  92. package/dist/browser/vm.test.js +398 -355
  93. package/dist/browser/vm.test.js.map +1 -1
  94. package/dist/node/cli.d.ts +1 -1
  95. package/dist/node/cli.js +34 -34
  96. package/dist/node/cli.js.map +1 -1
  97. package/dist/node/concisetext/ConciseTextLexer.d.ts +30 -30
  98. package/dist/node/concisetext/ConciseTextLexer.js +122 -122
  99. package/dist/node/concisetext/ConciseTextListener.d.ts +14 -14
  100. package/dist/node/concisetext/ConciseTextListener.js +35 -35
  101. package/dist/node/concisetext/ConciseTextParser.d.ts +99 -99
  102. package/dist/node/concisetext/ConciseTextParser.js +438 -438
  103. package/dist/node/getStackParams.d.ts +2 -2
  104. package/dist/node/getStackParams.js +23 -23
  105. package/dist/node/index.d.ts +5 -5
  106. package/dist/node/index.js +17 -17
  107. package/dist/node/interfaces.d.ts +42 -42
  108. package/dist/node/interfaces.js +2 -2
  109. package/dist/node/opcodes/and.d.ts +3 -3
  110. package/dist/node/opcodes/and.js +8 -8
  111. package/dist/node/opcodes/charCode.d.ts +3 -3
  112. package/dist/node/opcodes/charCode.js +9 -9
  113. package/dist/node/opcodes/closeBrace.d.ts +3 -3
  114. package/dist/node/opcodes/closeBrace.js +6 -6
  115. package/dist/node/opcodes/closeBrace.js.map +1 -1
  116. package/dist/node/opcodes/concat.d.ts +3 -3
  117. package/dist/node/opcodes/concat.js +8 -8
  118. package/dist/node/opcodes/delContext.d.ts +3 -3
  119. package/dist/node/opcodes/delContext.js +8 -8
  120. package/dist/node/opcodes/dup.d.ts +6 -6
  121. package/dist/node/opcodes/dup.js +12 -12
  122. package/dist/node/opcodes/eq.d.ts +3 -3
  123. package/dist/node/opcodes/eq.js +9 -9
  124. package/dist/node/opcodes/exit.d.ts +3 -3
  125. package/dist/node/opcodes/exit.js +6 -6
  126. package/dist/node/opcodes/getContext.d.ts +3 -3
  127. package/dist/node/opcodes/getContext.js +12 -12
  128. package/dist/node/opcodes/goto.d.ts +3 -3
  129. package/dist/node/opcodes/goto.js +19 -19
  130. package/dist/node/opcodes/gt.d.ts +3 -3
  131. package/dist/node/opcodes/gt.js +9 -9
  132. package/dist/node/opcodes/hasContext.d.ts +3 -3
  133. package/dist/node/opcodes/hasContext.js +13 -13
  134. package/dist/node/opcodes/jgz.d.ts +3 -3
  135. package/dist/node/opcodes/jgz.js +10 -10
  136. package/dist/node/opcodes/jz.d.ts +3 -3
  137. package/dist/node/opcodes/jz.js +10 -10
  138. package/dist/node/opcodes/lt.d.ts +3 -3
  139. package/dist/node/opcodes/lt.js +9 -9
  140. package/dist/node/opcodes/min.d.ts +3 -3
  141. package/dist/node/opcodes/min.js +10 -10
  142. package/dist/node/opcodes/mul.d.ts +3 -3
  143. package/dist/node/opcodes/mul.js +10 -10
  144. package/dist/node/opcodes/nop.d.ts +6 -6
  145. package/dist/node/opcodes/nop.js +9 -9
  146. package/dist/node/opcodes/not.d.ts +6 -6
  147. package/dist/node/opcodes/not.js +16 -16
  148. package/dist/node/opcodes/openBrace.d.ts +3 -3
  149. package/dist/node/opcodes/openBrace.js +20 -20
  150. package/dist/node/opcodes/openBrace.js.map +1 -1
  151. package/dist/node/opcodes/or.d.ts +3 -3
  152. package/dist/node/opcodes/or.js +8 -8
  153. package/dist/node/opcodes/pause.d.ts +3 -3
  154. package/dist/node/opcodes/pause.js +6 -6
  155. package/dist/node/opcodes/plus.d.ts +3 -3
  156. package/dist/node/opcodes/plus.js +10 -10
  157. package/dist/node/opcodes/pop.d.ts +3 -3
  158. package/dist/node/opcodes/pop.js +6 -6
  159. package/dist/node/opcodes/ppc.d.ts +6 -6
  160. package/dist/node/opcodes/ppc.js +9 -9
  161. package/dist/node/opcodes/randInt.d.ts +3 -3
  162. package/dist/node/opcodes/randInt.js +9 -9
  163. package/dist/node/opcodes/rconcat.d.ts +3 -3
  164. package/dist/node/opcodes/rconcat.js +8 -8
  165. package/dist/node/opcodes/setContext.d.ts +3 -3
  166. package/dist/node/opcodes/setContext.js +8 -8
  167. package/dist/node/opcodes/stacksize.d.ts +3 -3
  168. package/dist/node/opcodes/stacksize.js +6 -6
  169. package/dist/node/opcodes/stdout.d.ts +3 -3
  170. package/dist/node/opcodes/stdout.js +6 -6
  171. package/dist/node/util/builder.d.ts +12 -12
  172. package/dist/node/util/builder.js +37 -37
  173. package/dist/node/util/fix_antlr_grammar_ts.d.ts +1 -1
  174. package/dist/node/util/fix_antlr_grammar_ts.js +7 -7
  175. package/dist/node/util/tokenizer.d.ts +20 -20
  176. package/dist/node/util/tokenizer.js +176 -176
  177. package/dist/node/util/tokenizer.js.map +1 -1
  178. package/dist/node/vm.d.ts +26 -26
  179. package/dist/node/vm.js +201 -201
  180. package/dist/node/vm.test.d.ts +1 -1
  181. package/dist/node/vm.test.js +398 -355
  182. package/dist/node/vm.test.js.map +1 -1
  183. package/jest.config.js +4 -4
  184. package/package.json +50 -50
  185. package/tsconfig-browser.json +8 -8
  186. package/tsconfig.json +28 -28
@@ -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 add numbers', () => {
118
- expectStack(`1 2 +`, [3], "plus_0");
119
- expectStack(`1 2 plus`, [3], "plus_1");
120
- });
121
- test('should subtract numbers', () => {
122
- expectStack(`1 2 -`, [1], "min_0");
123
- expectStack(`1 2 min`, [1], "min_1");
124
- expectStack(`1 10 min`, [9], "min_2");
125
- });
126
- test('should multiply numbers', () => {
127
- expectStack(`2 3 *`, [6], "multiply_0");
128
- expectStack(`2 3 mul`, [6], "multiply_1");
129
- });
130
- test('should support the not instruction', () => {
131
- expectStack(`0 not`, [1], "not_0");
132
- expectStack(`1 not`, [0], "not_1");
133
- expectStack(`42 not`, [0], "not_2");
134
- });
135
- test('should support jgz, { and } for jgz-style if statements', () => {
136
- expectStack(`1 jgz { "EXPECT_THIS" } "AND_THIS"`, ["EXPECT_THIS", "AND_THIS"], "if_jgz_0");
137
- expectStack(`0 jgz { "EXPECT_NOT_THIS" } "EXPECT_ONLY_THIS"`, ["EXPECT_ONLY_THIS"], "if_jgz_1");
138
- });
139
- test('should support jz', () => {
140
- expectStack(`0 jz "no"`, [], "jz_0");
141
- expectStack(`1 jz "yes"`, ["yes"], "jz_1");
142
- expectStack(`-1 jz "yes"`, ["yes"], "jz_2");
143
- });
144
- test('should support jgz', () => {
145
- expectStack(`0 jgz "yes"`, ["yes"], "jgz_0");
146
- expectStack(`-1 jgz "yes"`, ["yes"], "jgz_1");
147
- expectStack(`1 jgz "no"`, [], "jgz_2");
148
- });
149
- test('should support braces', () => {
150
- expectStack(`1 2 { 3 4 5 } 6 7`, [1, 2, 6, 7], "braces");
151
- });
152
- test('should support nested braces', () => {
153
- expectStack(`1 2 { 3 { 4 5 } 6 } 7`, [1, 2, 7], "braces_nested");
154
- });
155
- test('should support the goto instruction', () => {
156
- expectStack(`5 goto "foo" "bar" "baz" "quux" exit`, ["quux"], "goto_by_number");
157
- expectStack(`"myLabel" goto "foo" "bar" "baz" "quux" #myLabel exit`, ["quux"], "goto_by_label");
158
- expect(createVMAndRunCode(`"myLabel" goto "foo" "bar" "baz" "quux" exit`, {}, { myLabel: 5 }).stack).toEqual(["quux"]);
159
- });
160
- test('should calculate equality between values on the stack', () => {
161
- expectStack(`1 1 eq`, [1], "eq_0"); // equal
162
- expectStack(`0 0 eq`, [1], "eq_1"); // equal
163
- expectStack(`"foo" "foo" eq`, [1], "eq_2"); // equal
164
- expectStack(`"foo" "bar" eq`, [0], "eq_3"); // not equal
165
- expectStack(`1 2 eq`, [0], "eq_4"); // not equal
166
- expectStack(`2 1 eq`, [0], "eq_5"); // not equal
167
- expectStack(`"0" 0 eq`, [0], "eq_6"); // not equal
168
- });
169
- test('should calculate greater than', () => {
170
- expectStack(`1 1 gt`, [0], 'gt_0'); // not greater than
171
- expectStack(`1 0 gt`, [0], 'gt_1'); // not greater than
172
- expectStack(`1 2 gt`, [1], 'gt_2'); // greater than
173
- expectStack(`1 -2 gt`, [0], 'gt_3'); // not greater than
174
- expectStack(`-1 2 gt`, [1], 'gt_4'); // greater than
175
- expectStack(`-1 -2 gt`, [0], 'gt_5'); // not greater than
176
- expectStack(`-2 -1 gt`, [1], 'gt_6'); // greater than
177
- expectStack(`-2 -2 gt`, [0], 'gt_7'); // not greater than
178
- });
179
- test('should calculate less than', () => {
180
- expectStack(`1 1 lt`, [0], 'lt_0'); // not less than
181
- expectStack(`1 2 lt`, [0], 'lt_1'); // not less than
182
- expectStack(`1 0 lt`, [1], 'lt_2'); // less than
183
- expectStack(`1 -1 lt`, [1], 'lt_3'); // less than
184
- expectStack(`-1 -1 lt`, [0], 'lt_4'); // not less than
185
- expectStack(`-1 -2 lt`, [1], 'lt_5'); // not less than
186
- expectStack(`-1 2 lt`, [0], 'lt_6'); // not less than
187
- expectStack(`2 -1 lt`, [1], 'lt_7'); // not less than
188
- });
189
- test('should concat', () => {
190
- expectStack(`"one" "two" concat`, ["twoone"], "concat_0");
191
- expectStack(`"1" "two" concat`, ["two1"], "concat_1");
192
- expectStack(`1 2 concat`, ["21"], "concat_2");
193
- expectStack(`"one" 2 concat`, ["2one"], "concat_3");
194
- });
195
- test('should rconcat (reverse concat)', () => {
196
- expectStack(`"one" "two" rconcat`, ["onetwo"], "rconcat_0");
197
- expectStack(`"1" "two" rconcat`, ["1two"], "rconcat_1");
198
- expectStack(`1 2 rconcat`, ["12"], "rconcat_2");
199
- expectStack(`"one" 2 rconcat`, ["one2"], "rconcat_3");
200
- });
201
- test('should pause', () => {
202
- const vm = createVMAndRunCode(`"foo" "bar" pause "baz" "quux"`);
203
- expect(vm.stack).toEqual(["foo", "bar"]);
204
- expect(vm.pause).toEqual(true);
205
- expect(vm.exit).toEqual(false);
206
- expect(vm.programCounter).toEqual(3); // pause WILL increment the programCounter!
207
- vm.run();
208
- expect(vm.stack).toEqual(["foo", "bar", "baz", "quux"]);
209
- expect(vm.pause).toEqual(false);
210
- expect(vm.exit).toEqual(true);
211
- expect(vm.programCounter).toEqual(5);
212
- });
213
- test('should exit', () => {
214
- const vm = createVMAndRunCode(`"foo" "bar" exit "baz" "quux"`);
215
- expect(vm.stack).toEqual(["foo", "bar"]);
216
- expect(vm.pause).toEqual(false);
217
- expect(vm.exit).toEqual(true);
218
- expect(vm.programCounter).toEqual(3); // exit WILL increment the programCounter!
219
- vm.run(); // this is a no-op, effectively, as the VM can not run anymore!
220
- expect(vm.stack).toEqual(["foo", "bar"]);
221
- expect(vm.pause).toEqual(false);
222
- expect(vm.exit).toEqual(true);
223
- expect(vm.programCounter).toEqual(3);
224
- });
225
- test('should push program counter on ppc instruction', () => {
226
- expectStack(`nop nop ppc`, [2], "ppc_0");
227
- expectStack(`ppc`, [0], "ppc_1");
228
- });
229
- test('should do nothing on nop instruction', () => {
230
- expectStack(`nop nop nop`, [], "nop_0");
231
- expectVM(`nop`, {}, { programCounter: 1, context: {}, stack: [] }, "nop_1");
232
- });
233
- test('should calculate and', () => {
234
- expectStack(`1 1 and`, [1], "and_0");
235
- expectStack(`1 0 and`, [0], "and_1");
236
- expectStack(`0 1 and`, [0], "and_2");
237
- expectStack(`0 0 and`, [0], "and_3");
238
- expectStack(`1 9 and`, [1], "and_4");
239
- expectStack(`9 1 and`, [1], "and_5");
240
- });
241
- test('should calculate or', () => {
242
- expectStack(`1 1 or`, [1], "or_0");
243
- expectStack(`1 0 or`, [1], "or_1");
244
- expectStack(`0 1 or`, [1], "or_2");
245
- expectStack(`0 0 or`, [0], "or_3");
246
- expectStack(`1 9 or`, [1], "or_4");
247
- expectStack(`9 1 or`, [1], "or_5");
248
- });
249
- test('should duplicate top item from stack via dup', () => {
250
- expectStack(`1 dup`, [1, 1], "dup_0");
251
- expectStack(`0 dup`, [0, 0], "dup_1");
252
- expectStack(`"test" dup`, ["test", "test"], "dup_2");
253
- expectStack(`42 dup`, [42, 42], "dup_3");
254
- });
255
- test('should pop top item from stack via pop', () => {
256
- expectStack(`1 pop`, [], "pop_0");
257
- expectStack(`0 pop`, [], "pop_1");
258
- expectStack(`"test" pop`, [], "pop_2");
259
- expectStack(`1 2 3 pop`, [1, 2], "pop_3");
260
- });
261
- test('should support labels via tokenizer', () => {
262
- const vm = createVMAndRunCode(`1 "Awesome" goto 2 3 #Awesome nop`);
263
- expect(vm.labelMap["Awesome"]).toBe(4);
264
- expect(vm.stack).toEqual([1, 3]);
265
- });
266
- test('should support the`stacksize opcode', () => {
267
- expectStack(`"f" "o" "o" stacksize`, ["f", "o", "o", 3], "stacksize_0");
268
- expectStack(`stacksize`, [0], "stacksize_1");
269
- });
270
- test('should support Standard Representation code', () => {
271
- expect(createVMAndRunStdRepCode([
272
- {
273
- "type": "push-number-instruction",
274
- "value": 42
275
- },
276
- {
277
- "type": "push-number-instruction",
278
- "value": 8
279
- },
280
- {
281
- "type": "invoke-function-instruction",
282
- "functionName": "+"
283
- },
284
- {
285
- "type": "push-string-instruction",
286
- "value": "Foo"
287
- },
288
- {
289
- "type": "invoke-function-instruction",
290
- "functionName": "setContext"
291
- }
292
- ]).context).toEqual({ "Foo": 50 });
293
- });
294
- test('should support labels in Standard Representation code', () => {
295
- expect(createVMAndRunStdRepCode([
296
- {
297
- "type": "push-number-instruction",
298
- "value": 1
299
- },
300
- {
301
- "type": "push-string-instruction",
302
- "value": "Awesome"
303
- },
304
- {
305
- "type": "invoke-function-instruction",
306
- "functionName": "goto"
307
- },
308
- {
309
- "type": "push-number-instruction",
310
- "value": 2,
311
- "comment": "Due to the goto above, this instruction will be skipped, and so 2 won't be pushed to the stack"
312
- },
313
- {
314
- "type": "push-number-instruction",
315
- "value": 3,
316
- "label": "Awesome"
317
- },
318
- {
319
- "type": "invoke-function-instruction",
320
- "functionName": "nop"
321
- }
322
- ]).stack).toEqual([1, 3]);
323
- });
324
- test('example tests', () => {
325
- expectStack(`1 1 + 2 eq jgz { "1 + 1 = 2!" }`, ["1 + 1 = 2!"], "example_0");
326
- expectStack(`1 1 + 2 eq dup jgz { "1 + 1 = 2!" pop } jz { "1 + 1 is not 2!?" }`, [], "example_1");
327
- 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");
328
- expectStack(`"Hello" "," " world!" rconcat rconcat`, ["Hello, world!"], "example_3");
329
- expectStack(`1 2 3 4 "hi" 0 -1 0 0 1 1 stacksize jgz { pop } stacksize jgz { 9 ppc - goto }`, [], "example_4");
330
- });
331
- test('poor man\'s function', () => {
332
- expectVM(`
333
- {
334
- nop #mul3
335
- "_mul3_return_pc" setContext
336
- 3 *
337
- "_mul3_return_pc" getContext 3 + "_mul3_return_pc" delContext goto
338
- }
339
- 1 ppc "mul3" goto
340
- 2 ppc "mul3" goto
341
- 3 ppc "mul3" goto
342
- 4 ppc "mul3" goto
343
- `, {}, {
344
- stack: [3, 6, 9, 12],
345
- context: {}
346
- }, 'poor_mans_function');
347
- });
348
- test('stdout', () => {
349
- const stdOut = jest_mock_process_1.mockProcessStdout();
350
- expectStack(`1 stdout`, [], "stdout_0");
351
- expect(stdOut).toHaveBeenCalledWith('1');
352
- expectStack(`"hello" stdout`, [], "stdout_1");
353
- expect(stdOut).toHaveBeenCalledWith('hello');
354
- stdOut.mockRestore();
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