@nudojs/cli 0.2.0 → 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/README.md +34 -0
- package/package.json +24 -4
- package/src/abs-route.ts +273 -0
- package/src/builtins/builtin-intl.ts +17 -0
- package/src/builtins/builtin-map.ts +174 -0
- package/src/builtins/builtin-promise.ts +116 -0
- package/src/builtins/builtin-prototype.ts +279 -0
- package/src/builtins/builtin-reflect.ts +30 -0
- package/src/builtins/builtin-regexp.ts +34 -0
- package/src/builtins/builtin-set.ts +131 -0
- package/src/builtins/builtin-static.ts +260 -0
- package/src/builtins/builtin-symbol.ts +16 -0
- package/src/builtins/builtin-url.ts +58 -0
- package/src/builtins/builtin-weak.ts +22 -0
- package/src/builtins/builtin-web.ts +111 -0
- package/src/config.ts +32 -0
- package/src/env-loader.ts +174 -0
- package/src/eval-binary.ts +113 -0
- package/src/evaluator-api.ts +23 -0
- package/src/evaluator.ts +3502 -285
- package/src/index.ts +922 -180
- package/src/narrowing.ts +237 -15
- package/src/phi-from-test.ts +42 -0
- package/src/resolve-npm.ts +63 -0
- package/src/run-test.ts +92 -0
- package/src/term-registry.ts +96 -0
- package/CHANGELOG.md +0 -29
- package/dist/chunk-LGWVGEMB.js +0 -1817
- package/dist/evaluator-api.d.ts +0 -59
- package/dist/evaluator-api.js +0 -30
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -3570
- package/src/__tests__/evaluator-loops.test.ts +0 -163
- package/src/__tests__/evaluator-phase2.test.ts +0 -564
- package/src/__tests__/evaluator-phase3.test.ts +0 -513
- 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 -243
- package/src/__tests__/narrowing.test.ts +0 -82
- package/tsconfig.json +0 -9
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { T, typeValueEquals, createEnvironment } from "@nudojs/core";
|
|
3
|
-
import { evaluateProgram } from "../evaluator.ts";
|
|
4
|
-
import { parse } from "@nudojs/parser";
|
|
5
|
-
import type { TypeValue } from "@nudojs/core";
|
|
6
|
-
|
|
7
|
-
function evalExpr(code: string): TypeValue {
|
|
8
|
-
const ast = parse(code);
|
|
9
|
-
const env = createEnvironment();
|
|
10
|
-
return evaluateProgram(ast, env);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function evalWith(code: string, bindings: Record<string, TypeValue>): TypeValue {
|
|
14
|
-
const ast = parse(code);
|
|
15
|
-
const env = createEnvironment();
|
|
16
|
-
for (const [k, v] of Object.entries(bindings)) env.bind(k, v);
|
|
17
|
-
return evaluateProgram(ast, env);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe("String methods - literal receiver", () => {
|
|
21
|
-
it("toUpperCase", () => {
|
|
22
|
-
expect(typeValueEquals(evalExpr('"hello".toUpperCase()'), T.literal("HELLO"))).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("toLowerCase", () => {
|
|
26
|
-
expect(typeValueEquals(evalExpr('"HELLO".toLowerCase()'), T.literal("hello"))).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("trim", () => {
|
|
30
|
-
expect(typeValueEquals(evalExpr('" hi ".trim()'), T.literal("hi"))).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("trimStart", () => {
|
|
34
|
-
expect(typeValueEquals(evalExpr('" hi".trimStart()'), T.literal("hi"))).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("trimEnd", () => {
|
|
38
|
-
expect(typeValueEquals(evalExpr('"hi ".trimEnd()'), T.literal("hi"))).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("charAt", () => {
|
|
42
|
-
expect(typeValueEquals(evalExpr('"abc".charAt(1)'), T.literal("b"))).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("charCodeAt", () => {
|
|
46
|
-
expect(typeValueEquals(evalExpr('"A".charCodeAt(0)'), T.literal(65))).toBe(true);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("at", () => {
|
|
50
|
-
expect(typeValueEquals(evalExpr('"abc".at(-1)'), T.literal("c"))).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("startsWith", () => {
|
|
54
|
-
expect(typeValueEquals(evalExpr('"hello".startsWith("he")'), T.literal(true))).toBe(true);
|
|
55
|
-
expect(typeValueEquals(evalExpr('"hello".startsWith("lo")'), T.literal(false))).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("endsWith", () => {
|
|
59
|
-
expect(typeValueEquals(evalExpr('"hello".endsWith("lo")'), T.literal(true))).toBe(true);
|
|
60
|
-
expect(typeValueEquals(evalExpr('"hello".endsWith("he")'), T.literal(false))).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("includes", () => {
|
|
64
|
-
expect(typeValueEquals(evalExpr('"hello".includes("ell")'), T.literal(true))).toBe(true);
|
|
65
|
-
expect(typeValueEquals(evalExpr('"hello".includes("xyz")'), T.literal(false))).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("indexOf", () => {
|
|
69
|
-
expect(typeValueEquals(evalExpr('"hello".indexOf("l")'), T.literal(2))).toBe(true);
|
|
70
|
-
expect(typeValueEquals(evalExpr('"hello".indexOf("z")'), T.literal(-1))).toBe(true);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("lastIndexOf", () => {
|
|
74
|
-
expect(typeValueEquals(evalExpr('"hello".lastIndexOf("l")'), T.literal(3))).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("slice", () => {
|
|
78
|
-
expect(typeValueEquals(evalExpr('"hello".slice(1, 3)'), T.literal("el"))).toBe(true);
|
|
79
|
-
expect(typeValueEquals(evalExpr('"hello".slice(2)'), T.literal("llo"))).toBe(true);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("substring", () => {
|
|
83
|
-
expect(typeValueEquals(evalExpr('"hello".substring(1, 4)'), T.literal("ell"))).toBe(true);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("split", () => {
|
|
87
|
-
const result = evalExpr('"a,b,c".split(",")');
|
|
88
|
-
expect(result.kind).toBe("tuple");
|
|
89
|
-
if (result.kind === "tuple") {
|
|
90
|
-
expect(result.elements.length).toBe(3);
|
|
91
|
-
expect(typeValueEquals(result.elements[0], T.literal("a"))).toBe(true);
|
|
92
|
-
expect(typeValueEquals(result.elements[1], T.literal("b"))).toBe(true);
|
|
93
|
-
expect(typeValueEquals(result.elements[2], T.literal("c"))).toBe(true);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("replace", () => {
|
|
98
|
-
expect(typeValueEquals(evalExpr('"hello".replace("l", "r")'), T.literal("herlo"))).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("replaceAll", () => {
|
|
102
|
-
expect(typeValueEquals(evalExpr('"hello".replaceAll("l", "r")'), T.literal("herro"))).toBe(true);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("repeat", () => {
|
|
106
|
-
expect(typeValueEquals(evalExpr('"ab".repeat(3)'), T.literal("ababab"))).toBe(true);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("padStart", () => {
|
|
110
|
-
expect(typeValueEquals(evalExpr('"5".padStart(3, "0")'), T.literal("005"))).toBe(true);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("padEnd", () => {
|
|
114
|
-
expect(typeValueEquals(evalExpr('"5".padEnd(3, "0")'), T.literal("500"))).toBe(true);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe("String methods - abstract receiver", () => {
|
|
119
|
-
it("toUpperCase returns string", () => {
|
|
120
|
-
const result = evalWith("x.toUpperCase()", { x: T.string });
|
|
121
|
-
expect(typeValueEquals(result, T.string)).toBe(true);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("startsWith returns boolean", () => {
|
|
125
|
-
const result = evalWith('x.startsWith("a")', { x: T.string });
|
|
126
|
-
expect(typeValueEquals(result, T.boolean)).toBe(true);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("indexOf returns number", () => {
|
|
130
|
-
const result = evalWith('x.indexOf("a")', { x: T.string });
|
|
131
|
-
expect(typeValueEquals(result, T.number)).toBe(true);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("split returns string[]", () => {
|
|
135
|
-
const result = evalWith('x.split(",")', { x: T.string });
|
|
136
|
-
expect(result.kind).toBe("array");
|
|
137
|
-
if (result.kind === "array") {
|
|
138
|
-
expect(typeValueEquals(result.element, T.string)).toBe(true);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("charAt returns string", () => {
|
|
143
|
-
const result = evalWith("x.charAt(0)", { x: T.string });
|
|
144
|
-
expect(typeValueEquals(result, T.string)).toBe(true);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it("slice returns string", () => {
|
|
148
|
-
const result = evalWith("x.slice(1)", { x: T.string });
|
|
149
|
-
expect(typeValueEquals(result, T.string)).toBe(true);
|
|
150
|
-
});
|
|
151
|
-
});
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { T, typeValueEquals, typeValueToString, createEnvironment } from "@nudojs/core";
|
|
3
|
-
import { parse, extractDirectives } from "@nudojs/parser";
|
|
4
|
-
import { evaluateFunction, evaluateProgram } from "../evaluator.ts";
|
|
5
|
-
|
|
6
|
-
function inferFromSource(source: string) {
|
|
7
|
-
const ast = parse(source);
|
|
8
|
-
const functions = extractDirectives(ast);
|
|
9
|
-
const env = createEnvironment();
|
|
10
|
-
return functions.map((fn) => ({
|
|
11
|
-
name: fn.name,
|
|
12
|
-
cases: fn.directives.map((d) => ({
|
|
13
|
-
caseName: d.name,
|
|
14
|
-
result: evaluateFunction(fn.node, d.args, env),
|
|
15
|
-
})),
|
|
16
|
-
}));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe("End-to-end: calc example from design doc", () => {
|
|
20
|
-
const source = `
|
|
21
|
-
/**
|
|
22
|
-
* @nudo:case "concrete" (1, 2)
|
|
23
|
-
* @nudo:case "symbolic" (T.number, T.number)
|
|
24
|
-
*/
|
|
25
|
-
function calc(a, b) {
|
|
26
|
-
if (a > b) return a - b;
|
|
27
|
-
return a + b;
|
|
28
|
-
}
|
|
29
|
-
`;
|
|
30
|
-
|
|
31
|
-
it("infers concrete case: calc(1, 2) => 3", () => {
|
|
32
|
-
const results = inferFromSource(source);
|
|
33
|
-
expect(results).toHaveLength(1);
|
|
34
|
-
expect(results[0].name).toBe("calc");
|
|
35
|
-
const concreteCase = results[0].cases[0];
|
|
36
|
-
expect(concreteCase.caseName).toBe("concrete");
|
|
37
|
-
expect(typeValueEquals(concreteCase.result, T.literal(3))).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("infers symbolic case: calc(number, number) => number", () => {
|
|
41
|
-
const results = inferFromSource(source);
|
|
42
|
-
const symbolicCase = results[0].cases[1];
|
|
43
|
-
expect(symbolicCase.caseName).toBe("symbolic");
|
|
44
|
-
expect(typeValueEquals(symbolicCase.result, T.number)).toBe(true);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("End-to-end: subtract example", () => {
|
|
49
|
-
const source = `
|
|
50
|
-
/**
|
|
51
|
-
* @nudo:case "positive numbers" (5, 3)
|
|
52
|
-
* @nudo:case "negative result" (1, 10)
|
|
53
|
-
* @nudo:case "symbolic" (T.number, T.number)
|
|
54
|
-
*/
|
|
55
|
-
function subtract(a, b) {
|
|
56
|
-
return a - b;
|
|
57
|
-
}
|
|
58
|
-
`;
|
|
59
|
-
|
|
60
|
-
it("infers subtract(5, 3) => 2", () => {
|
|
61
|
-
const results = inferFromSource(source);
|
|
62
|
-
const c = results[0].cases[0];
|
|
63
|
-
expect(typeValueEquals(c.result, T.literal(2))).toBe(true);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("infers subtract(1, 10) => -9", () => {
|
|
67
|
-
const results = inferFromSource(source);
|
|
68
|
-
const c = results[0].cases[1];
|
|
69
|
-
expect(typeValueEquals(c.result, T.literal(-9))).toBe(true);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("infers subtract(number, number) => number", () => {
|
|
73
|
-
const results = inferFromSource(source);
|
|
74
|
-
const c = results[0].cases[2];
|
|
75
|
-
expect(typeValueEquals(c.result, T.number)).toBe(true);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe("End-to-end: typeof narrowing", () => {
|
|
80
|
-
const source = `
|
|
81
|
-
/**
|
|
82
|
-
* @nudo:case "with number" (42)
|
|
83
|
-
* @nudo:case "with string" ("hello")
|
|
84
|
-
* @nudo:case "symbolic" (T.union(T.number, T.string))
|
|
85
|
-
*/
|
|
86
|
-
function describe(x) {
|
|
87
|
-
if (typeof x === "number") return x + 1;
|
|
88
|
-
return x;
|
|
89
|
-
}
|
|
90
|
-
`;
|
|
91
|
-
|
|
92
|
-
it("infers describe(42) => 43", () => {
|
|
93
|
-
const results = inferFromSource(source);
|
|
94
|
-
const c = results[0].cases[0];
|
|
95
|
-
expect(typeValueEquals(c.result, T.literal(43))).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("infers describe('hello') => 'hello'", () => {
|
|
99
|
-
const results = inferFromSource(source);
|
|
100
|
-
const c = results[0].cases[1];
|
|
101
|
-
expect(typeValueEquals(c.result, T.literal("hello"))).toBe(true);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("infers describe(number | string) => number | string", () => {
|
|
105
|
-
const results = inferFromSource(source);
|
|
106
|
-
const c = results[0].cases[2];
|
|
107
|
-
expect(typeValueToString(c.result)).toBe("number | string");
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe("End-to-end: strict equality narrowing", () => {
|
|
112
|
-
const source = `
|
|
113
|
-
/**
|
|
114
|
-
* @nudo:case "null case" (null)
|
|
115
|
-
* @nudo:case "number case" (5)
|
|
116
|
-
* @nudo:case "symbolic" (T.union(T.null, T.number))
|
|
117
|
-
*/
|
|
118
|
-
function safe(x) {
|
|
119
|
-
if (x === null) return 0;
|
|
120
|
-
return x;
|
|
121
|
-
}
|
|
122
|
-
`;
|
|
123
|
-
|
|
124
|
-
it("infers safe(null) => 0", () => {
|
|
125
|
-
const results = inferFromSource(source);
|
|
126
|
-
const c = results[0].cases[0];
|
|
127
|
-
expect(typeValueEquals(c.result, T.literal(0))).toBe(true);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("infers safe(5) => 5", () => {
|
|
131
|
-
const results = inferFromSource(source);
|
|
132
|
-
const c = results[0].cases[1];
|
|
133
|
-
expect(typeValueEquals(c.result, T.literal(5))).toBe(true);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("infers safe(null | number) => 0 | number", () => {
|
|
137
|
-
const results = inferFromSource(source);
|
|
138
|
-
const c = results[0].cases[2];
|
|
139
|
-
const str = typeValueToString(c.result);
|
|
140
|
-
expect(str === "0 | number" || str === "number | 0").toBe(true);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("Evaluator: basic expressions", () => {
|
|
145
|
-
it("evaluates arithmetic", () => {
|
|
146
|
-
const ast = parse("1 + 2 * 3");
|
|
147
|
-
const env = createEnvironment();
|
|
148
|
-
const result = evaluateProgram(ast, env);
|
|
149
|
-
expect(typeValueEquals(result, T.literal(7))).toBe(true);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("evaluates variable declaration and usage", () => {
|
|
153
|
-
const ast = parse("const x = 10; x + 5;");
|
|
154
|
-
const env = createEnvironment();
|
|
155
|
-
const result = evaluateProgram(ast, env);
|
|
156
|
-
expect(typeValueEquals(result, T.literal(15))).toBe(true);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("evaluates ternary expression", () => {
|
|
160
|
-
const ast = parse("true ? 1 : 2");
|
|
161
|
-
const env = createEnvironment();
|
|
162
|
-
const result = evaluateProgram(ast, env);
|
|
163
|
-
expect(typeValueEquals(result, T.literal(1))).toBe(true);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("evaluates object literal", () => {
|
|
167
|
-
const ast = parse('const obj = { x: 1, y: "hi" }; obj.x;');
|
|
168
|
-
const env = createEnvironment();
|
|
169
|
-
const result = evaluateProgram(ast, env);
|
|
170
|
-
expect(typeValueEquals(result, T.literal(1))).toBe(true);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it("evaluates array literal", () => {
|
|
174
|
-
const ast = parse("const arr = [1, 2, 3]; arr.length;");
|
|
175
|
-
const env = createEnvironment();
|
|
176
|
-
const result = evaluateProgram(ast, env);
|
|
177
|
-
expect(typeValueEquals(result, T.literal(3))).toBe(true);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("evaluates function call", () => {
|
|
181
|
-
const ast = parse(`
|
|
182
|
-
function add(a, b) { return a + b; }
|
|
183
|
-
add(3, 4);
|
|
184
|
-
`);
|
|
185
|
-
const env = createEnvironment();
|
|
186
|
-
const result = evaluateProgram(ast, env);
|
|
187
|
-
expect(typeValueEquals(result, T.literal(7))).toBe(true);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("evaluates arrow function", () => {
|
|
191
|
-
const ast = parse(`
|
|
192
|
-
const double = (x) => x + x;
|
|
193
|
-
double(5);
|
|
194
|
-
`);
|
|
195
|
-
const env = createEnvironment();
|
|
196
|
-
const result = evaluateProgram(ast, env);
|
|
197
|
-
expect(typeValueEquals(result, T.literal(10))).toBe(true);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("evaluates logical operators", () => {
|
|
201
|
-
const ast = parse("true && 42");
|
|
202
|
-
const env = createEnvironment();
|
|
203
|
-
const result = evaluateProgram(ast, env);
|
|
204
|
-
expect(typeValueEquals(result, T.literal(42))).toBe(true);
|
|
205
|
-
|
|
206
|
-
const ast2 = parse("false || 99");
|
|
207
|
-
const result2 = evaluateProgram(ast2, createEnvironment());
|
|
208
|
-
expect(typeValueEquals(result2, T.literal(99))).toBe(true);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("evaluates nullish coalescing", () => {
|
|
212
|
-
const ast = parse("null ?? 42");
|
|
213
|
-
const env = createEnvironment();
|
|
214
|
-
const result = evaluateProgram(ast, env);
|
|
215
|
-
expect(typeValueEquals(result, T.literal(42))).toBe(true);
|
|
216
|
-
|
|
217
|
-
const ast2 = parse("1 ?? 42");
|
|
218
|
-
const result2 = evaluateProgram(ast2, createEnvironment());
|
|
219
|
-
expect(typeValueEquals(result2, T.literal(1))).toBe(true);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it("evaluates unary operators", () => {
|
|
223
|
-
const ast = parse("typeof 42");
|
|
224
|
-
const env = createEnvironment();
|
|
225
|
-
const result = evaluateProgram(ast, env);
|
|
226
|
-
expect(typeValueEquals(result, T.literal("number"))).toBe(true);
|
|
227
|
-
|
|
228
|
-
const ast2 = parse("!true");
|
|
229
|
-
const result2 = evaluateProgram(ast2, createEnvironment());
|
|
230
|
-
expect(typeValueEquals(result2, T.literal(false))).toBe(true);
|
|
231
|
-
|
|
232
|
-
const ast3 = parse("-5");
|
|
233
|
-
const result3 = evaluateProgram(ast3, createEnvironment());
|
|
234
|
-
expect(typeValueEquals(result3, T.literal(-5))).toBe(true);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it("evaluates template literal", () => {
|
|
238
|
-
const ast = parse("`hello`");
|
|
239
|
-
const env = createEnvironment();
|
|
240
|
-
const result = evaluateProgram(ast, env);
|
|
241
|
-
expect(typeValueEquals(result, T.literal("hello"))).toBe(true);
|
|
242
|
-
});
|
|
243
|
-
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { T, typeValueEquals, typeValueToString, createEnvironment } from "@nudojs/core";
|
|
3
|
-
import { parse } from "@nudojs/parser";
|
|
4
|
-
import { narrow } from "../narrowing.ts";
|
|
5
|
-
import type { ExpressionStatement } from "@babel/types";
|
|
6
|
-
|
|
7
|
-
function getTestExpr(source: string) {
|
|
8
|
-
const ast = parse(source);
|
|
9
|
-
const stmt = ast.program.body[0] as ExpressionStatement;
|
|
10
|
-
return stmt.expression;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
describe("narrow: typeof", () => {
|
|
14
|
-
it("narrows typeof x === 'number' on union", () => {
|
|
15
|
-
const env = createEnvironment();
|
|
16
|
-
env.bind("x", T.union(T.number, T.string));
|
|
17
|
-
const expr = getTestExpr('typeof x === "number"');
|
|
18
|
-
const [trueEnv, falseEnv] = narrow(expr, env);
|
|
19
|
-
expect(typeValueEquals(trueEnv.lookup("x"), T.number)).toBe(true);
|
|
20
|
-
expect(typeValueEquals(falseEnv.lookup("x"), T.string)).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("narrows typeof x === 'string' on union", () => {
|
|
24
|
-
const env = createEnvironment();
|
|
25
|
-
env.bind("x", T.union(T.number, T.string, T.boolean));
|
|
26
|
-
const expr = getTestExpr('typeof x === "string"');
|
|
27
|
-
const [trueEnv, falseEnv] = narrow(expr, env);
|
|
28
|
-
expect(typeValueEquals(trueEnv.lookup("x"), T.string)).toBe(true);
|
|
29
|
-
const falseType = falseEnv.lookup("x");
|
|
30
|
-
expect(typeValueToString(falseType)).toBe("number | boolean");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("narrows typeof x !== 'number' (inverted)", () => {
|
|
34
|
-
const env = createEnvironment();
|
|
35
|
-
env.bind("x", T.union(T.number, T.string));
|
|
36
|
-
const expr = getTestExpr('typeof x !== "number"');
|
|
37
|
-
const [trueEnv, falseEnv] = narrow(expr, env);
|
|
38
|
-
expect(typeValueEquals(trueEnv.lookup("x"), T.string)).toBe(true);
|
|
39
|
-
expect(typeValueEquals(falseEnv.lookup("x"), T.number)).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("narrow: strict equality", () => {
|
|
44
|
-
it("narrows x === null", () => {
|
|
45
|
-
const env = createEnvironment();
|
|
46
|
-
env.bind("x", T.union(T.null, T.number));
|
|
47
|
-
const expr = getTestExpr("x === null");
|
|
48
|
-
const [trueEnv, falseEnv] = narrow(expr, env);
|
|
49
|
-
expect(typeValueEquals(trueEnv.lookup("x"), T.null)).toBe(true);
|
|
50
|
-
expect(typeValueEquals(falseEnv.lookup("x"), T.number)).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("narrows x === 1", () => {
|
|
54
|
-
const env = createEnvironment();
|
|
55
|
-
env.bind("x", T.union(T.literal(1), T.literal(2), T.literal(3)));
|
|
56
|
-
const expr = getTestExpr("x === 1");
|
|
57
|
-
const [trueEnv, falseEnv] = narrow(expr, env);
|
|
58
|
-
expect(typeValueEquals(trueEnv.lookup("x"), T.literal(1))).toBe(true);
|
|
59
|
-
const falseType = falseEnv.lookup("x");
|
|
60
|
-
expect(typeValueToString(falseType)).toBe("2 | 3");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("narrows x !== 1 (inverted)", () => {
|
|
64
|
-
const env = createEnvironment();
|
|
65
|
-
env.bind("x", T.union(T.literal(1), T.literal(2)));
|
|
66
|
-
const expr = getTestExpr("x !== 1");
|
|
67
|
-
const [trueEnv, falseEnv] = narrow(expr, env);
|
|
68
|
-
expect(typeValueEquals(trueEnv.lookup("x"), T.literal(2))).toBe(true);
|
|
69
|
-
expect(typeValueEquals(falseEnv.lookup("x"), T.literal(1))).toBe(true);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe("narrow: negation", () => {
|
|
74
|
-
it("narrows !(typeof x === 'number')", () => {
|
|
75
|
-
const env = createEnvironment();
|
|
76
|
-
env.bind("x", T.union(T.number, T.string));
|
|
77
|
-
const expr = getTestExpr('!(typeof x === "number")');
|
|
78
|
-
const [trueEnv, falseEnv] = narrow(expr, env);
|
|
79
|
-
expect(typeValueEquals(trueEnv.lookup("x"), T.string)).toBe(true);
|
|
80
|
-
expect(typeValueEquals(falseEnv.lookup("x"), T.number)).toBe(true);
|
|
81
|
-
});
|
|
82
|
-
});
|