@nudojs/cli 0.2.1 → 0.3.0
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/package.json +12 -7
- package/src/abs-route.ts +273 -0
- package/src/builtins/builtin-prototype.ts +279 -0
- package/src/builtins/builtin-static.ts +260 -0
- package/src/eval-binary.ts +113 -0
- package/src/evaluator-api.ts +3 -0
- package/src/evaluator.ts +174 -633
- package/src/index.ts +373 -270
- package/src/narrowing.ts +7 -0
- package/src/phi-from-test.ts +42 -0
- package/src/run-test.ts +92 -0
- package/src/term-registry.ts +96 -0
- package/CHANGELOG.md +0 -41
- package/dist/chunk-3HCPXPEW.js +0 -5286
- package/dist/evaluator-api.d.ts +0 -151
- package/dist/evaluator-api.js +0 -214
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -4035
- package/examples/real-world.js +0 -66
- package/examples/validate-real-world.ts +0 -160
- package/src/__tests__/builtin-advanced.test.ts +0 -292
- package/src/__tests__/builtin-api.test.ts +0 -308
- package/src/__tests__/builtin-functions.test.ts +0 -313
- package/src/__tests__/builtin-map.test.ts +0 -129
- package/src/__tests__/builtin-promise.test.ts +0 -150
- package/src/__tests__/builtin-regexp.test.ts +0 -133
- package/src/__tests__/builtin-set.test.ts +0 -163
- package/src/__tests__/builtin-url.test.ts +0 -256
- package/src/__tests__/builtin-web.test.ts +0 -340
- package/src/__tests__/combination-scenarios.test.ts +0 -384
- package/src/__tests__/edge-cases.test.ts +0 -749
- package/src/__tests__/evaluator-loops.test.ts +0 -283
- package/src/__tests__/evaluator-nullish-coalescing.test.ts +0 -86
- package/src/__tests__/evaluator-optional-chaining.test.ts +0 -37
- package/src/__tests__/evaluator-phase2.test.ts +0 -564
- package/src/__tests__/evaluator-phase3.test.ts +0 -864
- package/src/__tests__/evaluator-refined.test.ts +0 -121
- package/src/__tests__/evaluator-string-methods.test.ts +0 -151
- package/src/__tests__/evaluator.test.ts +0 -943
- package/src/__tests__/mock-arrow-function.test.ts +0 -169
- package/src/__tests__/mock-comparison.test.ts +0 -221
- package/src/__tests__/mock-comprehensive.test.ts +0 -250
- package/src/__tests__/mock-final-integration.test.ts +0 -263
- package/src/__tests__/mock-helpers.test.ts +0 -174
- package/src/__tests__/mock-realworld.test.ts +0 -191
- package/src/__tests__/mock-rest-debug.test.ts +0 -110
- package/src/__tests__/mock-rest-edge.test.ts +0 -176
- package/src/__tests__/mock-sinon-runtime.test.ts +0 -168
- package/src/__tests__/narrowing-discriminated.test.ts +0 -35
- package/src/__tests__/narrowing-in-operator.test.ts +0 -56
- package/src/__tests__/narrowing-switch.test.ts +0 -56
- package/src/__tests__/narrowing-truthiness.test.ts +0 -52
- package/src/__tests__/narrowing.test.ts +0 -82
- package/src/__tests__/nudo-mock-syntax.test.ts +0 -245
- package/src/__tests__/realworld-patterns.test.ts +0 -430
- package/src/__tests__/syntax-sugar.test.ts +0 -345
- package/src/builtins/index.ts +0 -61
- package/tsconfig.json +0 -9
|
@@ -1,564 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { T, typeValueEquals, typeValueToString, createEnvironment, isSubtypeOf } from "@nudojs/core";
|
|
3
|
-
import { parse } from "@nudojs/parser";
|
|
4
|
-
import { evaluateFunction, evaluateProgram } from "../evaluator.ts";
|
|
5
|
-
|
|
6
|
-
function evalExpr(source: string) {
|
|
7
|
-
const ast = parse(source);
|
|
8
|
-
return evaluateProgram(ast, createEnvironment());
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// --- Destructuring ---
|
|
12
|
-
|
|
13
|
-
describe("Object destructuring", () => {
|
|
14
|
-
it("basic object destructuring", () => {
|
|
15
|
-
const result = evalExpr(`
|
|
16
|
-
const obj = { x: 1, y: 2 };
|
|
17
|
-
const { x, y } = obj;
|
|
18
|
-
x + y;
|
|
19
|
-
`);
|
|
20
|
-
expect(typeValueEquals(result, T.literal(3))).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("destructuring with default value", () => {
|
|
24
|
-
const result = evalExpr(`
|
|
25
|
-
const obj = { x: 1 };
|
|
26
|
-
const { x, y = 10 } = obj;
|
|
27
|
-
y;
|
|
28
|
-
`);
|
|
29
|
-
expect(typeValueEquals(result, T.literal(10))).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("destructuring uses value when present (not default)", () => {
|
|
33
|
-
const result = evalExpr(`
|
|
34
|
-
const obj = { x: 1, y: 5 };
|
|
35
|
-
const { y = 10 } = obj;
|
|
36
|
-
y;
|
|
37
|
-
`);
|
|
38
|
-
expect(typeValueEquals(result, T.literal(5))).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("nested object destructuring", () => {
|
|
42
|
-
const result = evalExpr(`
|
|
43
|
-
const obj = { a: { b: 42 } };
|
|
44
|
-
const { a: { b } } = obj;
|
|
45
|
-
b;
|
|
46
|
-
`);
|
|
47
|
-
expect(typeValueEquals(result, T.literal(42))).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("rest element in object destructuring", () => {
|
|
51
|
-
const result = evalExpr(`
|
|
52
|
-
const obj = { x: 1, y: 2, z: 3 };
|
|
53
|
-
const { x, ...rest } = obj;
|
|
54
|
-
rest;
|
|
55
|
-
`);
|
|
56
|
-
if (result.kind === "object") {
|
|
57
|
-
expect(typeValueEquals(result.properties.y, T.literal(2))).toBe(true);
|
|
58
|
-
expect(typeValueEquals(result.properties.z, T.literal(3))).toBe(true);
|
|
59
|
-
expect(result.properties.x).toBeUndefined();
|
|
60
|
-
} else {
|
|
61
|
-
expect(result.kind).toBe("object");
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe("Array destructuring", () => {
|
|
67
|
-
it("basic array destructuring", () => {
|
|
68
|
-
const result = evalExpr(`
|
|
69
|
-
const arr = [1, 2, 3];
|
|
70
|
-
const [a, b, c] = arr;
|
|
71
|
-
a + b + c;
|
|
72
|
-
`);
|
|
73
|
-
expect(typeValueEquals(result, T.literal(6))).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("array destructuring with skip", () => {
|
|
77
|
-
const result = evalExpr(`
|
|
78
|
-
const arr = [1, 2, 3];
|
|
79
|
-
const [, , c] = arr;
|
|
80
|
-
c;
|
|
81
|
-
`);
|
|
82
|
-
expect(typeValueEquals(result, T.literal(3))).toBe(true);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("array destructuring with rest", () => {
|
|
86
|
-
const result = evalExpr(`
|
|
87
|
-
const arr = [1, 2, 3, 4];
|
|
88
|
-
const [first, ...rest] = arr;
|
|
89
|
-
rest;
|
|
90
|
-
`);
|
|
91
|
-
if (result.kind === "tuple") {
|
|
92
|
-
expect(result.elements).toHaveLength(3);
|
|
93
|
-
expect(typeValueEquals(result.elements[0], T.literal(2))).toBe(true);
|
|
94
|
-
} else {
|
|
95
|
-
expect(result.kind).toBe("tuple");
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("array destructuring with default", () => {
|
|
100
|
-
const result = evalExpr(`
|
|
101
|
-
const arr = [1];
|
|
102
|
-
const [a, b = 99] = arr;
|
|
103
|
-
b;
|
|
104
|
-
`);
|
|
105
|
-
expect(typeValueEquals(result, T.literal(99))).toBe(true);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe("Destructuring in function params", () => {
|
|
110
|
-
it("object destructuring in function params", () => {
|
|
111
|
-
const result = evalExpr(`
|
|
112
|
-
function getX({ x }) { return x; }
|
|
113
|
-
getX({ x: 42, y: 10 });
|
|
114
|
-
`);
|
|
115
|
-
expect(typeValueEquals(result, T.literal(42))).toBe(true);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("array destructuring in function params", () => {
|
|
119
|
-
const result = evalExpr(`
|
|
120
|
-
const getFirst = ([a, b]) => a + b;
|
|
121
|
-
getFirst([3, 7]);
|
|
122
|
-
`);
|
|
123
|
-
expect(typeValueEquals(result, T.literal(10))).toBe(true);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("default values in function param destructuring", () => {
|
|
127
|
-
const result = evalExpr(`
|
|
128
|
-
function greet({ name = "world" }) { return name; }
|
|
129
|
-
greet({});
|
|
130
|
-
`);
|
|
131
|
-
expect(typeValueEquals(result, T.literal("world"))).toBe(true);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// --- Spread operator ---
|
|
136
|
-
|
|
137
|
-
describe("Spread in object expression", () => {
|
|
138
|
-
it("spreads object properties", () => {
|
|
139
|
-
const result = evalExpr(`
|
|
140
|
-
const a = { x: 1, y: 2 };
|
|
141
|
-
const b = { ...a, z: 3 };
|
|
142
|
-
b;
|
|
143
|
-
`);
|
|
144
|
-
if (result.kind === "object") {
|
|
145
|
-
expect(typeValueEquals(result.properties.x, T.literal(1))).toBe(true);
|
|
146
|
-
expect(typeValueEquals(result.properties.y, T.literal(2))).toBe(true);
|
|
147
|
-
expect(typeValueEquals(result.properties.z, T.literal(3))).toBe(true);
|
|
148
|
-
} else {
|
|
149
|
-
expect(result.kind).toBe("object");
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it("later properties override spread", () => {
|
|
154
|
-
const result = evalExpr(`
|
|
155
|
-
const a = { x: 1, y: 2 };
|
|
156
|
-
const b = { ...a, x: 99 };
|
|
157
|
-
b.x;
|
|
158
|
-
`);
|
|
159
|
-
expect(typeValueEquals(result, T.literal(99))).toBe(true);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
describe("Spread in array expression", () => {
|
|
164
|
-
it("spreads tuple into array", () => {
|
|
165
|
-
const result = evalExpr(`
|
|
166
|
-
const a = [1, 2];
|
|
167
|
-
const b = [...a, 3, 4];
|
|
168
|
-
b;
|
|
169
|
-
`);
|
|
170
|
-
if (result.kind === "tuple") {
|
|
171
|
-
expect(result.elements).toHaveLength(4);
|
|
172
|
-
expect(typeValueEquals(result.elements[0], T.literal(1))).toBe(true);
|
|
173
|
-
expect(typeValueEquals(result.elements[3], T.literal(4))).toBe(true);
|
|
174
|
-
} else {
|
|
175
|
-
expect(result.kind).toBe("tuple");
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it("spreads at beginning and end", () => {
|
|
180
|
-
const result = evalExpr(`
|
|
181
|
-
const a = [2, 3];
|
|
182
|
-
const b = [1, ...a, 4];
|
|
183
|
-
b;
|
|
184
|
-
`);
|
|
185
|
-
if (result.kind === "tuple") {
|
|
186
|
-
expect(result.elements).toHaveLength(4);
|
|
187
|
-
expect(typeValueEquals(result.elements[0], T.literal(1))).toBe(true);
|
|
188
|
-
expect(typeValueEquals(result.elements[1], T.literal(2))).toBe(true);
|
|
189
|
-
} else {
|
|
190
|
-
expect(result.kind).toBe("tuple");
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// --- Mutability tracking ---
|
|
196
|
-
|
|
197
|
-
describe("Object mutability", () => {
|
|
198
|
-
it("MemberExpression assignment modifies object in place", () => {
|
|
199
|
-
const result = evalExpr(`
|
|
200
|
-
const obj = { x: 1 };
|
|
201
|
-
obj.x = 2;
|
|
202
|
-
obj.x;
|
|
203
|
-
`);
|
|
204
|
-
expect(typeValueEquals(result, T.literal(2))).toBe(true);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("alias shares reference", () => {
|
|
208
|
-
const result = evalExpr(`
|
|
209
|
-
const obj = { x: 1 };
|
|
210
|
-
const alias = obj;
|
|
211
|
-
alias.x = 99;
|
|
212
|
-
obj.x;
|
|
213
|
-
`);
|
|
214
|
-
expect(typeValueEquals(result, T.literal(99))).toBe(true);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it("adding new property to object", () => {
|
|
218
|
-
const result = evalExpr(`
|
|
219
|
-
const obj = { x: 1 };
|
|
220
|
-
obj.y = 2;
|
|
221
|
-
obj.y;
|
|
222
|
-
`);
|
|
223
|
-
expect(typeValueEquals(result, T.literal(2))).toBe(true);
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// --- Array.prototype methods ---
|
|
228
|
-
|
|
229
|
-
describe("Array.prototype.map", () => {
|
|
230
|
-
it("maps over tuple with literal callback", () => {
|
|
231
|
-
const result = evalExpr(`
|
|
232
|
-
const arr = [1, 2, 3];
|
|
233
|
-
arr.map((x) => x + 10);
|
|
234
|
-
`);
|
|
235
|
-
if (result.kind === "tuple") {
|
|
236
|
-
expect(result.elements).toHaveLength(3);
|
|
237
|
-
expect(typeValueEquals(result.elements[0], T.literal(11))).toBe(true);
|
|
238
|
-
expect(typeValueEquals(result.elements[1], T.literal(12))).toBe(true);
|
|
239
|
-
expect(typeValueEquals(result.elements[2], T.literal(13))).toBe(true);
|
|
240
|
-
} else {
|
|
241
|
-
expect(result.kind).toBe("tuple");
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("maps over tuple producing different types", () => {
|
|
246
|
-
const result = evalExpr(`
|
|
247
|
-
const arr = [1, "hello", true];
|
|
248
|
-
arr.map((x) => typeof x);
|
|
249
|
-
`);
|
|
250
|
-
if (result.kind === "tuple") {
|
|
251
|
-
expect(typeValueEquals(result.elements[0], T.literal("number"))).toBe(true);
|
|
252
|
-
expect(typeValueEquals(result.elements[1], T.literal("string"))).toBe(true);
|
|
253
|
-
expect(typeValueEquals(result.elements[2], T.literal("boolean"))).toBe(true);
|
|
254
|
-
} else {
|
|
255
|
-
expect(result.kind).toBe("tuple");
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe("Array.prototype.filter", () => {
|
|
261
|
-
it("filters tuple with concrete predicate", () => {
|
|
262
|
-
const result = evalExpr(`
|
|
263
|
-
const arr = [1, 2, 3, 4];
|
|
264
|
-
arr.filter((x) => x > 2);
|
|
265
|
-
`);
|
|
266
|
-
expect(result.kind).toBe("array");
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it("filters tuple removing known-false elements", () => {
|
|
270
|
-
const result = evalExpr(`
|
|
271
|
-
const arr = [1, 2, 3];
|
|
272
|
-
arr.filter((x) => x === 2);
|
|
273
|
-
`);
|
|
274
|
-
expect(result.kind).toBe("array");
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
describe("Array.prototype.reduce", () => {
|
|
279
|
-
it("reduces tuple with initial value", () => {
|
|
280
|
-
const result = evalExpr(`
|
|
281
|
-
const arr = [1, 2, 3];
|
|
282
|
-
arr.reduce((acc, x) => acc + x, 0);
|
|
283
|
-
`);
|
|
284
|
-
expect(typeValueEquals(result, T.literal(6))).toBe(true);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it("reduces tuple without initial value", () => {
|
|
288
|
-
const result = evalExpr(`
|
|
289
|
-
const arr = [10, 20, 30];
|
|
290
|
-
arr.reduce((acc, x) => acc + x);
|
|
291
|
-
`);
|
|
292
|
-
expect(typeValueEquals(result, T.literal(60))).toBe(true);
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
describe("Array.prototype.find", () => {
|
|
297
|
-
it("find returns element | undefined", () => {
|
|
298
|
-
const result = evalExpr(`
|
|
299
|
-
const arr = [1, 2, 3];
|
|
300
|
-
arr.find((x) => x > 1);
|
|
301
|
-
`);
|
|
302
|
-
const str = typeValueToString(result);
|
|
303
|
-
expect(str).toContain("undefined");
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
describe("Array.prototype.some/every", () => {
|
|
308
|
-
it("some on tuple with concrete values", () => {
|
|
309
|
-
const result = evalExpr(`
|
|
310
|
-
const arr = [1, 2, 3];
|
|
311
|
-
arr.some((x) => x === 2);
|
|
312
|
-
`);
|
|
313
|
-
expect(typeValueEquals(result, T.literal(true))).toBe(true);
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it("every on tuple with concrete values", () => {
|
|
317
|
-
const result = evalExpr(`
|
|
318
|
-
const arr = [2, 4, 6];
|
|
319
|
-
arr.every((x) => x > 0);
|
|
320
|
-
`);
|
|
321
|
-
expect(typeValueEquals(result, T.literal(true))).toBe(true);
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
it("every returns false when not all match", () => {
|
|
325
|
-
const result = evalExpr(`
|
|
326
|
-
const arr = [2, 4, -1];
|
|
327
|
-
arr.every((x) => x > 0);
|
|
328
|
-
`);
|
|
329
|
-
expect(typeValueEquals(result, T.literal(false))).toBe(true);
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
describe("Array.prototype.push", () => {
|
|
334
|
-
it("push modifies tuple in place", () => {
|
|
335
|
-
const result = evalExpr(`
|
|
336
|
-
const arr = [1, 2];
|
|
337
|
-
arr.push(3);
|
|
338
|
-
arr;
|
|
339
|
-
`);
|
|
340
|
-
if (result.kind === "tuple") {
|
|
341
|
-
expect(result.elements).toHaveLength(3);
|
|
342
|
-
expect(typeValueEquals(result.elements[2], T.literal(3))).toBe(true);
|
|
343
|
-
} else {
|
|
344
|
-
expect(result.kind).toBe("tuple");
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it("push returns new length", () => {
|
|
349
|
-
const result = evalExpr(`
|
|
350
|
-
const arr = [1, 2];
|
|
351
|
-
arr.push(3);
|
|
352
|
-
`);
|
|
353
|
-
expect(typeValueEquals(result, T.literal(3))).toBe(true);
|
|
354
|
-
});
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
describe("Array.prototype misc", () => {
|
|
358
|
-
it("forEach returns undefined", () => {
|
|
359
|
-
const result = evalExpr(`
|
|
360
|
-
const arr = [1, 2, 3];
|
|
361
|
-
arr.forEach((x) => x + 1);
|
|
362
|
-
`);
|
|
363
|
-
expect(typeValueEquals(result, T.undefined)).toBe(true);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it("includes with literal value", () => {
|
|
367
|
-
const result = evalExpr(`
|
|
368
|
-
const arr = [1, 2, 3];
|
|
369
|
-
arr.includes(2);
|
|
370
|
-
`);
|
|
371
|
-
expect(typeValueEquals(result, T.literal(true))).toBe(true);
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it("indexOf returns number", () => {
|
|
375
|
-
const result = evalExpr(`
|
|
376
|
-
const arr = [1, 2, 3];
|
|
377
|
-
arr.indexOf(2);
|
|
378
|
-
`);
|
|
379
|
-
expect(typeValueEquals(result, T.number)).toBe(true);
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
it("join returns string", () => {
|
|
383
|
-
const result = evalExpr(`
|
|
384
|
-
const arr = [1, 2, 3];
|
|
385
|
-
arr.join(",");
|
|
386
|
-
`);
|
|
387
|
-
expect(typeValueEquals(result, T.string)).toBe(true);
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
it("slice on tuple", () => {
|
|
391
|
-
const result = evalExpr(`
|
|
392
|
-
const arr = [1, 2, 3, 4];
|
|
393
|
-
arr.slice(1, 3);
|
|
394
|
-
`);
|
|
395
|
-
if (result.kind === "tuple") {
|
|
396
|
-
expect(result.elements).toHaveLength(2);
|
|
397
|
-
expect(typeValueEquals(result.elements[0], T.literal(2))).toBe(true);
|
|
398
|
-
expect(typeValueEquals(result.elements[1], T.literal(3))).toBe(true);
|
|
399
|
-
} else {
|
|
400
|
-
expect(result.kind).toBe("tuple");
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
it("concat tuples", () => {
|
|
405
|
-
const result = evalExpr(`
|
|
406
|
-
const a = [1, 2];
|
|
407
|
-
const b = [3, 4];
|
|
408
|
-
a.concat(b);
|
|
409
|
-
`);
|
|
410
|
-
if (result.kind === "tuple") {
|
|
411
|
-
expect(result.elements).toHaveLength(4);
|
|
412
|
-
} else {
|
|
413
|
-
expect(result.kind).toBe("tuple");
|
|
414
|
-
}
|
|
415
|
-
});
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
// --- Object static methods ---
|
|
419
|
-
|
|
420
|
-
describe("Object.keys", () => {
|
|
421
|
-
it("returns tuple of literal keys for known object", () => {
|
|
422
|
-
const result = evalExpr(`
|
|
423
|
-
const obj = { x: 1, y: 2, z: 3 };
|
|
424
|
-
Object.keys(obj);
|
|
425
|
-
`);
|
|
426
|
-
if (result.kind === "tuple") {
|
|
427
|
-
expect(result.elements).toHaveLength(3);
|
|
428
|
-
expect(typeValueEquals(result.elements[0], T.literal("x"))).toBe(true);
|
|
429
|
-
expect(typeValueEquals(result.elements[1], T.literal("y"))).toBe(true);
|
|
430
|
-
expect(typeValueEquals(result.elements[2], T.literal("z"))).toBe(true);
|
|
431
|
-
} else {
|
|
432
|
-
expect(result.kind).toBe("tuple");
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
describe("Object.values", () => {
|
|
438
|
-
it("returns tuple of values for known object", () => {
|
|
439
|
-
const result = evalExpr(`
|
|
440
|
-
const obj = { x: 1, y: "hello" };
|
|
441
|
-
Object.values(obj);
|
|
442
|
-
`);
|
|
443
|
-
if (result.kind === "tuple") {
|
|
444
|
-
expect(result.elements).toHaveLength(2);
|
|
445
|
-
expect(typeValueEquals(result.elements[0], T.literal(1))).toBe(true);
|
|
446
|
-
expect(typeValueEquals(result.elements[1], T.literal("hello"))).toBe(true);
|
|
447
|
-
} else {
|
|
448
|
-
expect(result.kind).toBe("tuple");
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
describe("Object.entries", () => {
|
|
454
|
-
it("returns tuple of [key, value] tuples", () => {
|
|
455
|
-
const result = evalExpr(`
|
|
456
|
-
const obj = { a: 1, b: 2 };
|
|
457
|
-
Object.entries(obj);
|
|
458
|
-
`);
|
|
459
|
-
if (result.kind === "tuple") {
|
|
460
|
-
expect(result.elements).toHaveLength(2);
|
|
461
|
-
const first = result.elements[0];
|
|
462
|
-
if (first.kind === "tuple") {
|
|
463
|
-
expect(typeValueEquals(first.elements[0], T.literal("a"))).toBe(true);
|
|
464
|
-
expect(typeValueEquals(first.elements[1], T.literal(1))).toBe(true);
|
|
465
|
-
}
|
|
466
|
-
} else {
|
|
467
|
-
expect(result.kind).toBe("tuple");
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
// --- for-of / for-in ---
|
|
473
|
-
|
|
474
|
-
describe("for-of loop", () => {
|
|
475
|
-
it("iterates over tuple elements", () => {
|
|
476
|
-
const result = evalExpr(`
|
|
477
|
-
const arr = [1, 2, 3];
|
|
478
|
-
let sum = 0;
|
|
479
|
-
for (const x of arr) {
|
|
480
|
-
sum = sum + x;
|
|
481
|
-
}
|
|
482
|
-
sum;
|
|
483
|
-
`);
|
|
484
|
-
expect(typeValueEquals(result, T.literal(6))).toBe(true);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
it("for-of with destructuring", () => {
|
|
488
|
-
const result = evalExpr(`
|
|
489
|
-
const pairs = [[1, "a"], [2, "b"]];
|
|
490
|
-
let total = 0;
|
|
491
|
-
for (const [num] of pairs) {
|
|
492
|
-
total = total + num;
|
|
493
|
-
}
|
|
494
|
-
total;
|
|
495
|
-
`);
|
|
496
|
-
expect(typeValueEquals(result, T.literal(3))).toBe(true);
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
describe("for-in loop", () => {
|
|
501
|
-
it("iterates over object keys", () => {
|
|
502
|
-
const result = evalExpr(`
|
|
503
|
-
const obj = { a: 1, b: 2, c: 3 };
|
|
504
|
-
const keys = [];
|
|
505
|
-
for (const k in obj) {
|
|
506
|
-
keys.push(k);
|
|
507
|
-
}
|
|
508
|
-
keys;
|
|
509
|
-
`);
|
|
510
|
-
if (result.kind === "tuple") {
|
|
511
|
-
expect(result.elements).toHaveLength(3);
|
|
512
|
-
expect(typeValueEquals(result.elements[0], T.literal("a"))).toBe(true);
|
|
513
|
-
} else {
|
|
514
|
-
expect(result.kind).toBe("tuple");
|
|
515
|
-
}
|
|
516
|
-
});
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
// --- Integration: combining features ---
|
|
520
|
-
|
|
521
|
-
describe("Integration: map + destructuring", () => {
|
|
522
|
-
it("maps objects and destructures", () => {
|
|
523
|
-
const result = evalExpr(`
|
|
524
|
-
const items = [{ name: "a", value: 1 }, { name: "b", value: 2 }];
|
|
525
|
-
items.map(({ name, value }) => value + 10);
|
|
526
|
-
`);
|
|
527
|
-
if (result.kind === "tuple") {
|
|
528
|
-
expect(result.elements).toHaveLength(2);
|
|
529
|
-
expect(typeValueEquals(result.elements[0], T.literal(11))).toBe(true);
|
|
530
|
-
expect(typeValueEquals(result.elements[1], T.literal(12))).toBe(true);
|
|
531
|
-
} else {
|
|
532
|
-
expect(result.kind).toBe("tuple");
|
|
533
|
-
}
|
|
534
|
-
});
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
describe("Integration: spread + Object methods", () => {
|
|
538
|
-
it("Object.keys after spread merge", () => {
|
|
539
|
-
const result = evalExpr(`
|
|
540
|
-
const a = { x: 1 };
|
|
541
|
-
const b = { y: 2 };
|
|
542
|
-
const merged = { ...a, ...b };
|
|
543
|
-
Object.keys(merged);
|
|
544
|
-
`);
|
|
545
|
-
if (result.kind === "tuple") {
|
|
546
|
-
expect(result.elements).toHaveLength(2);
|
|
547
|
-
} else {
|
|
548
|
-
expect(result.kind).toBe("tuple");
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
describe("Integration: reduce with object accumulator", () => {
|
|
554
|
-
it("builds object via reduce", () => {
|
|
555
|
-
const result = evalExpr(`
|
|
556
|
-
const pairs = [["a", 1], ["b", 2]];
|
|
557
|
-
pairs.reduce((acc, [key, val]) => {
|
|
558
|
-
acc[key] = val;
|
|
559
|
-
return acc;
|
|
560
|
-
}, {});
|
|
561
|
-
`);
|
|
562
|
-
expect(result.kind).toBe("object");
|
|
563
|
-
});
|
|
564
|
-
});
|