@tsonic/frontend 0.0.5 → 0.0.12
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/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ir/builder/imports.d.ts.map +1 -1
- package/dist/ir/builder/imports.js +0 -5
- package/dist/ir/builder/imports.js.map +1 -1
- package/dist/ir/builder.test.js +60 -0
- package/dist/ir/builder.test.js.map +1 -1
- package/dist/ir/converters/expressions/access.d.ts.map +1 -1
- package/dist/ir/converters/expressions/access.js +65 -2
- package/dist/ir/converters/expressions/access.js.map +1 -1
- package/dist/ir/converters/expressions/calls.d.ts.map +1 -1
- package/dist/ir/converters/expressions/calls.js +49 -1
- package/dist/ir/converters/expressions/calls.js.map +1 -1
- package/dist/ir/converters/expressions/collections.d.ts.map +1 -1
- package/dist/ir/converters/expressions/collections.js +4 -1
- package/dist/ir/converters/expressions/collections.js.map +1 -1
- package/dist/ir/converters/expressions/functions.d.ts.map +1 -1
- package/dist/ir/converters/expressions/functions.js +3 -1
- package/dist/ir/converters/expressions/functions.js.map +1 -1
- package/dist/ir/converters/expressions/helpers.d.ts +10 -0
- package/dist/ir/converters/expressions/helpers.d.ts.map +1 -1
- package/dist/ir/converters/expressions/helpers.js +173 -7
- package/dist/ir/converters/expressions/helpers.js.map +1 -1
- package/dist/ir/converters/expressions/index.d.ts +1 -1
- package/dist/ir/converters/expressions/index.d.ts.map +1 -1
- package/dist/ir/converters/expressions/index.js +1 -1
- package/dist/ir/converters/expressions/index.js.map +1 -1
- package/dist/ir/converters/expressions/literals.d.ts.map +1 -1
- package/dist/ir/converters/expressions/literals.js +2 -1
- package/dist/ir/converters/expressions/literals.js.map +1 -1
- package/dist/ir/converters/expressions/numeric-recovery.test.d.ts +14 -0
- package/dist/ir/converters/expressions/numeric-recovery.test.d.ts.map +1 -0
- package/dist/ir/converters/expressions/numeric-recovery.test.js +286 -0
- package/dist/ir/converters/expressions/numeric-recovery.test.js.map +1 -0
- package/dist/ir/converters/expressions/operators.d.ts.map +1 -1
- package/dist/ir/converters/expressions/operators.js +16 -2
- package/dist/ir/converters/expressions/operators.js.map +1 -1
- package/dist/ir/converters/expressions/other.d.ts.map +1 -1
- package/dist/ir/converters/expressions/other.js +4 -1
- package/dist/ir/converters/expressions/other.js.map +1 -1
- package/dist/ir/converters/statements/control/loops.d.ts.map +1 -1
- package/dist/ir/converters/statements/control/loops.js +1 -0
- package/dist/ir/converters/statements/control/loops.js.map +1 -1
- package/dist/ir/expression-converter.d.ts.map +1 -1
- package/dist/ir/expression-converter.js +81 -9
- package/dist/ir/expression-converter.js.map +1 -1
- package/dist/ir/type-converter/arrays.d.ts.map +1 -1
- package/dist/ir/type-converter/arrays.js +1 -0
- package/dist/ir/type-converter/arrays.js.map +1 -1
- package/dist/ir/type-converter/inference.d.ts.map +1 -1
- package/dist/ir/type-converter/inference.js +31 -20
- package/dist/ir/type-converter/inference.js.map +1 -1
- package/dist/ir/type-converter/literals.d.ts.map +1 -1
- package/dist/ir/type-converter/literals.js +2 -1
- package/dist/ir/type-converter/literals.js.map +1 -1
- package/dist/ir/type-converter/objects.d.ts.map +1 -1
- package/dist/ir/type-converter/objects.js +2 -1
- package/dist/ir/type-converter/objects.js.map +1 -1
- package/dist/ir/type-converter/orchestrator.d.ts.map +1 -1
- package/dist/ir/type-converter/orchestrator.js +18 -1
- package/dist/ir/type-converter/orchestrator.js.map +1 -1
- package/dist/ir/type-converter/references.d.ts.map +1 -1
- package/dist/ir/type-converter/references.js +14 -18
- package/dist/ir/type-converter/references.js.map +1 -1
- package/dist/ir/types/expressions.d.ts +102 -1
- package/dist/ir/types/expressions.d.ts.map +1 -1
- package/dist/ir/types/guards.d.ts.map +1 -1
- package/dist/ir/types/guards.js +2 -0
- package/dist/ir/types/guards.js.map +1 -1
- package/dist/ir/types/index.d.ts +4 -2
- package/dist/ir/types/index.d.ts.map +1 -1
- package/dist/ir/types/index.js +1 -0
- package/dist/ir/types/index.js.map +1 -1
- package/dist/ir/types/ir-types.d.ts +19 -0
- package/dist/ir/types/ir-types.d.ts.map +1 -1
- package/dist/ir/types/numeric-kind.d.ts +68 -0
- package/dist/ir/types/numeric-kind.d.ts.map +1 -0
- package/dist/ir/types/numeric-kind.js +170 -0
- package/dist/ir/types/numeric-kind.js.map +1 -0
- package/dist/ir/types/statements.d.ts +38 -1
- package/dist/ir/types/statements.d.ts.map +1 -1
- package/dist/ir/types.d.ts +2 -1
- package/dist/ir/types.d.ts.map +1 -1
- package/dist/ir/types.js +2 -0
- package/dist/ir/types.js.map +1 -1
- package/dist/ir/validation/index.d.ts +7 -0
- package/dist/ir/validation/index.d.ts.map +1 -0
- package/dist/ir/validation/index.js +7 -0
- package/dist/ir/validation/index.js.map +1 -0
- package/dist/ir/validation/numeric-invariants.test.d.ts +15 -0
- package/dist/ir/validation/numeric-invariants.test.d.ts.map +1 -0
- package/dist/ir/validation/numeric-invariants.test.js +598 -0
- package/dist/ir/validation/numeric-invariants.test.js.map +1 -0
- package/dist/ir/validation/numeric-proof-pass.d.ts +37 -0
- package/dist/ir/validation/numeric-proof-pass.d.ts.map +1 -0
- package/dist/ir/validation/numeric-proof-pass.js +889 -0
- package/dist/ir/validation/numeric-proof-pass.js.map +1 -0
- package/dist/ir/validation/soundness-gate.d.ts +26 -0
- package/dist/ir/validation/soundness-gate.d.ts.map +1 -0
- package/dist/ir/validation/soundness-gate.js +551 -0
- package/dist/ir/validation/soundness-gate.js.map +1 -0
- package/dist/ir/validation/soundness-gate.test.d.ts +13 -0
- package/dist/ir/validation/soundness-gate.test.d.ts.map +1 -0
- package/dist/ir/validation/soundness-gate.test.js +315 -0
- package/dist/ir/validation/soundness-gate.test.js.map +1 -0
- package/dist/ir/validation/yield-lowering-pass.d.ts +40 -0
- package/dist/ir/validation/yield-lowering-pass.d.ts.map +1 -0
- package/dist/ir/validation/yield-lowering-pass.js +548 -0
- package/dist/ir/validation/yield-lowering-pass.js.map +1 -0
- package/dist/ir/validation/yield-lowering-pass.test.d.ts +12 -0
- package/dist/ir/validation/yield-lowering-pass.test.d.ts.map +1 -0
- package/dist/ir/validation/yield-lowering-pass.test.js +761 -0
- package/dist/ir/validation/yield-lowering-pass.test.js.map +1 -0
- package/dist/program/bindings.d.ts +5 -0
- package/dist/program/bindings.d.ts.map +1 -1
- package/dist/program/bindings.js +12 -1
- package/dist/program/bindings.js.map +1 -1
- package/dist/program/dependency-graph.d.ts +3 -0
- package/dist/program/dependency-graph.d.ts.map +1 -1
- package/dist/program/dependency-graph.js +28 -4
- package/dist/program/dependency-graph.js.map +1 -1
- package/dist/program/index.d.ts +1 -1
- package/dist/program/index.d.ts.map +1 -1
- package/dist/program/index.js.map +1 -1
- package/dist/program.d.ts +1 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js.map +1 -1
- package/dist/types/diagnostic.d.ts +1 -1
- package/dist/types/diagnostic.d.ts.map +1 -1
- package/dist/types/diagnostic.js.map +1 -1
- package/package.json +1 -1
- package/dist/types/parameter-modifiers.d.ts +0 -55
- package/dist/types/parameter-modifiers.d.ts.map +0 -1
- package/dist/types/parameter-modifiers.js +0 -148
- package/dist/types/parameter-modifiers.js.map +0 -1
|
@@ -0,0 +1,761 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Yield Lowering Pass
|
|
3
|
+
*
|
|
4
|
+
* Tests:
|
|
5
|
+
* - containsYield detection
|
|
6
|
+
* - countYields counting
|
|
7
|
+
* - Pattern transformations (yield expr, const x = yield, assignment)
|
|
8
|
+
* - Unsupported position detection (TSN6101)
|
|
9
|
+
* - Non-generator function handling (no transformation)
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it } from "mocha";
|
|
12
|
+
import { expect } from "chai";
|
|
13
|
+
import { runYieldLoweringPass } from "./yield-lowering-pass.js";
|
|
14
|
+
/**
|
|
15
|
+
* Helper to create a minimal generator function module
|
|
16
|
+
*/
|
|
17
|
+
const createGeneratorModule = (body, options = {}) => ({
|
|
18
|
+
kind: "module",
|
|
19
|
+
filePath: "/src/test.ts",
|
|
20
|
+
namespace: "Test",
|
|
21
|
+
className: "test",
|
|
22
|
+
isStaticContainer: true,
|
|
23
|
+
imports: [],
|
|
24
|
+
body: [
|
|
25
|
+
{
|
|
26
|
+
kind: "functionDeclaration",
|
|
27
|
+
name: "testGen",
|
|
28
|
+
parameters: [],
|
|
29
|
+
returnType: options.returnType ?? {
|
|
30
|
+
kind: "referenceType",
|
|
31
|
+
name: "Generator",
|
|
32
|
+
typeArguments: [
|
|
33
|
+
{ kind: "primitiveType", name: "number" },
|
|
34
|
+
{ kind: "voidType" },
|
|
35
|
+
{ kind: "primitiveType", name: "number" },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
body: { kind: "blockStatement", statements: body },
|
|
39
|
+
isAsync: false,
|
|
40
|
+
isGenerator: options.isGenerator ?? true,
|
|
41
|
+
isExported: true,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
exports: [],
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* Helper to create a yield expression
|
|
48
|
+
*/
|
|
49
|
+
const createYield = (value, delegate = false) => ({
|
|
50
|
+
kind: "yield",
|
|
51
|
+
expression: value,
|
|
52
|
+
delegate,
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Helper to extract the function body from a module
|
|
56
|
+
*/
|
|
57
|
+
const getGeneratorBody = (module) => {
|
|
58
|
+
const func = module.body[0];
|
|
59
|
+
if (func?.kind === "functionDeclaration") {
|
|
60
|
+
return func.body.statements;
|
|
61
|
+
}
|
|
62
|
+
return [];
|
|
63
|
+
};
|
|
64
|
+
describe("Yield Lowering Pass", () => {
|
|
65
|
+
describe("Basic Yield Transformation", () => {
|
|
66
|
+
it("should transform simple yield expr into yieldStatement", () => {
|
|
67
|
+
const module = createGeneratorModule([
|
|
68
|
+
{
|
|
69
|
+
kind: "expressionStatement",
|
|
70
|
+
expression: createYield({ kind: "literal", value: 42 }),
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
const result = runYieldLoweringPass([module]);
|
|
74
|
+
expect(result.ok).to.be.true;
|
|
75
|
+
expect(result.diagnostics).to.have.length(0);
|
|
76
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
77
|
+
expect(body).to.have.length(1);
|
|
78
|
+
expect(body[0]?.kind).to.equal("yieldStatement");
|
|
79
|
+
const yieldStmt = body[0];
|
|
80
|
+
expect(yieldStmt.delegate).to.be.false;
|
|
81
|
+
expect(yieldStmt.output?.kind).to.equal("literal");
|
|
82
|
+
expect(yieldStmt.receiveTarget).to.be.undefined;
|
|
83
|
+
});
|
|
84
|
+
it("should transform bare yield (no value)", () => {
|
|
85
|
+
const module = createGeneratorModule([
|
|
86
|
+
{
|
|
87
|
+
kind: "expressionStatement",
|
|
88
|
+
expression: createYield(),
|
|
89
|
+
},
|
|
90
|
+
]);
|
|
91
|
+
const result = runYieldLoweringPass([module]);
|
|
92
|
+
expect(result.ok).to.be.true;
|
|
93
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
94
|
+
const yieldStmt = body[0];
|
|
95
|
+
expect(yieldStmt.kind).to.equal("yieldStatement");
|
|
96
|
+
expect(yieldStmt.output).to.be.undefined;
|
|
97
|
+
});
|
|
98
|
+
it("should transform yield* delegation", () => {
|
|
99
|
+
const module = createGeneratorModule([
|
|
100
|
+
{
|
|
101
|
+
kind: "expressionStatement",
|
|
102
|
+
expression: createYield({ kind: "identifier", name: "otherGen" }, true),
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
const result = runYieldLoweringPass([module]);
|
|
106
|
+
expect(result.ok).to.be.true;
|
|
107
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
108
|
+
const yieldStmt = body[0];
|
|
109
|
+
expect(yieldStmt.delegate).to.be.true;
|
|
110
|
+
expect(yieldStmt.output?.kind).to.equal("identifier");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe("Bidirectional Pattern: const x = yield value", () => {
|
|
114
|
+
it("should transform variable declaration with yield initializer", () => {
|
|
115
|
+
const module = createGeneratorModule([
|
|
116
|
+
{
|
|
117
|
+
kind: "variableDeclaration",
|
|
118
|
+
declarationKind: "const",
|
|
119
|
+
isExported: false,
|
|
120
|
+
declarations: [
|
|
121
|
+
{
|
|
122
|
+
kind: "variableDeclarator",
|
|
123
|
+
name: { kind: "identifierPattern", name: "received" },
|
|
124
|
+
type: { kind: "primitiveType", name: "number" },
|
|
125
|
+
initializer: createYield({ kind: "literal", value: 10 }),
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
const result = runYieldLoweringPass([module]);
|
|
131
|
+
expect(result.ok).to.be.true;
|
|
132
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
133
|
+
expect(body).to.have.length(1);
|
|
134
|
+
const yieldStmt = body[0];
|
|
135
|
+
expect(yieldStmt.kind).to.equal("yieldStatement");
|
|
136
|
+
expect(yieldStmt.receiveTarget?.kind).to.equal("identifierPattern");
|
|
137
|
+
expect(yieldStmt.receiveTarget.name).to.equal("received");
|
|
138
|
+
expect(yieldStmt.output?.kind).to.equal("literal");
|
|
139
|
+
});
|
|
140
|
+
it("should handle multiple declarations with yield", () => {
|
|
141
|
+
const module = createGeneratorModule([
|
|
142
|
+
{
|
|
143
|
+
kind: "variableDeclaration",
|
|
144
|
+
declarationKind: "const",
|
|
145
|
+
isExported: false,
|
|
146
|
+
declarations: [
|
|
147
|
+
{
|
|
148
|
+
kind: "variableDeclarator",
|
|
149
|
+
name: { kind: "identifierPattern", name: "a" },
|
|
150
|
+
type: { kind: "primitiveType", name: "number" },
|
|
151
|
+
initializer: createYield({ kind: "literal", value: 1 }),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
kind: "variableDeclarator",
|
|
155
|
+
name: { kind: "identifierPattern", name: "b" },
|
|
156
|
+
type: { kind: "primitiveType", name: "number" },
|
|
157
|
+
initializer: createYield({ kind: "literal", value: 2 }),
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
]);
|
|
162
|
+
const result = runYieldLoweringPass([module]);
|
|
163
|
+
expect(result.ok).to.be.true;
|
|
164
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
165
|
+
// Should produce two yieldStatements
|
|
166
|
+
expect(body).to.have.length(2);
|
|
167
|
+
expect(body[0]?.kind).to.equal("yieldStatement");
|
|
168
|
+
expect(body[1]?.kind).to.equal("yieldStatement");
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe("Bidirectional Pattern: x = yield value", () => {
|
|
172
|
+
it("should transform assignment with yield on right side", () => {
|
|
173
|
+
const module = createGeneratorModule([
|
|
174
|
+
{
|
|
175
|
+
kind: "expressionStatement",
|
|
176
|
+
expression: {
|
|
177
|
+
kind: "assignment",
|
|
178
|
+
operator: "=",
|
|
179
|
+
left: { kind: "identifierPattern", name: "x" },
|
|
180
|
+
right: createYield({ kind: "literal", value: 5 }),
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
]);
|
|
184
|
+
const result = runYieldLoweringPass([module]);
|
|
185
|
+
expect(result.ok).to.be.true;
|
|
186
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
187
|
+
const yieldStmt = body[0];
|
|
188
|
+
expect(yieldStmt.kind).to.equal("yieldStatement");
|
|
189
|
+
expect(yieldStmt.receiveTarget?.kind).to.equal("identifierPattern");
|
|
190
|
+
});
|
|
191
|
+
it("should reject compound assignment with yield", () => {
|
|
192
|
+
const module = createGeneratorModule([
|
|
193
|
+
{
|
|
194
|
+
kind: "expressionStatement",
|
|
195
|
+
expression: {
|
|
196
|
+
kind: "assignment",
|
|
197
|
+
operator: "+=",
|
|
198
|
+
left: { kind: "identifierPattern", name: "x" },
|
|
199
|
+
right: createYield({ kind: "literal", value: 5 }),
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
]);
|
|
203
|
+
const result = runYieldLoweringPass([module]);
|
|
204
|
+
expect(result.ok).to.be.false;
|
|
205
|
+
expect(result.diagnostics).to.have.length(1);
|
|
206
|
+
expect(result.diagnostics[0]?.code).to.equal("TSN6101");
|
|
207
|
+
expect(result.diagnostics[0]?.message).to.include("compound assignment");
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe("Destructuring Patterns", () => {
|
|
211
|
+
it("should transform array destructuring with yield", () => {
|
|
212
|
+
const module = createGeneratorModule([
|
|
213
|
+
{
|
|
214
|
+
kind: "variableDeclaration",
|
|
215
|
+
declarationKind: "const",
|
|
216
|
+
isExported: false,
|
|
217
|
+
declarations: [
|
|
218
|
+
{
|
|
219
|
+
kind: "variableDeclarator",
|
|
220
|
+
name: {
|
|
221
|
+
kind: "arrayPattern",
|
|
222
|
+
elements: [
|
|
223
|
+
{ kind: "identifierPattern", name: "a" },
|
|
224
|
+
{ kind: "identifierPattern", name: "b" },
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
initializer: createYield({ kind: "literal", value: null }),
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
]);
|
|
232
|
+
const result = runYieldLoweringPass([module]);
|
|
233
|
+
expect(result.ok).to.be.true;
|
|
234
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
235
|
+
const yieldStmt = body[0];
|
|
236
|
+
expect(yieldStmt.receiveTarget?.kind).to.equal("arrayPattern");
|
|
237
|
+
});
|
|
238
|
+
it("should transform object destructuring with yield", () => {
|
|
239
|
+
const module = createGeneratorModule([
|
|
240
|
+
{
|
|
241
|
+
kind: "variableDeclaration",
|
|
242
|
+
declarationKind: "const",
|
|
243
|
+
isExported: false,
|
|
244
|
+
declarations: [
|
|
245
|
+
{
|
|
246
|
+
kind: "variableDeclarator",
|
|
247
|
+
name: {
|
|
248
|
+
kind: "objectPattern",
|
|
249
|
+
properties: [
|
|
250
|
+
{
|
|
251
|
+
kind: "property",
|
|
252
|
+
key: "x",
|
|
253
|
+
value: { kind: "identifierPattern", name: "x" },
|
|
254
|
+
shorthand: true,
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
initializer: createYield({ kind: "literal", value: null }),
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
]);
|
|
263
|
+
const result = runYieldLoweringPass([module]);
|
|
264
|
+
expect(result.ok).to.be.true;
|
|
265
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
266
|
+
const yieldStmt = body[0];
|
|
267
|
+
expect(yieldStmt.receiveTarget?.kind).to.equal("objectPattern");
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
describe("Unsupported Patterns (TSN6101)", () => {
|
|
271
|
+
it("should reject yield in call argument", () => {
|
|
272
|
+
const module = createGeneratorModule([
|
|
273
|
+
{
|
|
274
|
+
kind: "expressionStatement",
|
|
275
|
+
expression: {
|
|
276
|
+
kind: "call",
|
|
277
|
+
callee: { kind: "identifier", name: "foo" },
|
|
278
|
+
arguments: [createYield({ kind: "literal", value: 1 })],
|
|
279
|
+
isOptional: false,
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
]);
|
|
283
|
+
const result = runYieldLoweringPass([module]);
|
|
284
|
+
expect(result.ok).to.be.false;
|
|
285
|
+
expect(result.diagnostics[0]?.code).to.equal("TSN6101");
|
|
286
|
+
});
|
|
287
|
+
it("should reject nested yield in initializer", () => {
|
|
288
|
+
const module = createGeneratorModule([
|
|
289
|
+
{
|
|
290
|
+
kind: "variableDeclaration",
|
|
291
|
+
declarationKind: "const",
|
|
292
|
+
isExported: false,
|
|
293
|
+
declarations: [
|
|
294
|
+
{
|
|
295
|
+
kind: "variableDeclarator",
|
|
296
|
+
name: { kind: "identifierPattern", name: "x" },
|
|
297
|
+
type: { kind: "primitiveType", name: "number" },
|
|
298
|
+
initializer: {
|
|
299
|
+
kind: "binary",
|
|
300
|
+
operator: "+",
|
|
301
|
+
left: createYield({ kind: "literal", value: 1 }),
|
|
302
|
+
right: { kind: "literal", value: 2 },
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
]);
|
|
308
|
+
const result = runYieldLoweringPass([module]);
|
|
309
|
+
expect(result.ok).to.be.false;
|
|
310
|
+
expect(result.diagnostics[0]?.code).to.equal("TSN6101");
|
|
311
|
+
});
|
|
312
|
+
it("should reject yield in return expression", () => {
|
|
313
|
+
const module = createGeneratorModule([
|
|
314
|
+
{
|
|
315
|
+
kind: "returnStatement",
|
|
316
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
317
|
+
},
|
|
318
|
+
]);
|
|
319
|
+
const result = runYieldLoweringPass([module]);
|
|
320
|
+
expect(result.ok).to.be.false;
|
|
321
|
+
expect(result.diagnostics[0]?.code).to.equal("TSN6101");
|
|
322
|
+
});
|
|
323
|
+
it("should reject yield in throw expression", () => {
|
|
324
|
+
const module = createGeneratorModule([
|
|
325
|
+
{
|
|
326
|
+
kind: "throwStatement",
|
|
327
|
+
expression: createYield({ kind: "identifier", name: "err" }),
|
|
328
|
+
},
|
|
329
|
+
]);
|
|
330
|
+
const result = runYieldLoweringPass([module]);
|
|
331
|
+
expect(result.ok).to.be.false;
|
|
332
|
+
expect(result.diagnostics[0]?.code).to.equal("TSN6101");
|
|
333
|
+
});
|
|
334
|
+
it("should reject yield in for loop condition", () => {
|
|
335
|
+
const module = createGeneratorModule([
|
|
336
|
+
{
|
|
337
|
+
kind: "forStatement",
|
|
338
|
+
condition: createYield({ kind: "literal", value: true }),
|
|
339
|
+
body: { kind: "blockStatement", statements: [] },
|
|
340
|
+
},
|
|
341
|
+
]);
|
|
342
|
+
const result = runYieldLoweringPass([module]);
|
|
343
|
+
expect(result.ok).to.be.false;
|
|
344
|
+
expect(result.diagnostics[0]?.code).to.equal("TSN6101");
|
|
345
|
+
});
|
|
346
|
+
it("should reject yield in for loop update", () => {
|
|
347
|
+
const module = createGeneratorModule([
|
|
348
|
+
{
|
|
349
|
+
kind: "forStatement",
|
|
350
|
+
condition: { kind: "literal", value: true },
|
|
351
|
+
update: createYield({ kind: "literal", value: 1 }),
|
|
352
|
+
body: { kind: "blockStatement", statements: [] },
|
|
353
|
+
},
|
|
354
|
+
]);
|
|
355
|
+
const result = runYieldLoweringPass([module]);
|
|
356
|
+
expect(result.ok).to.be.false;
|
|
357
|
+
expect(result.diagnostics[0]?.code).to.equal("TSN6101");
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
describe("Control Flow Structures", () => {
|
|
361
|
+
it("should transform yield inside if statement", () => {
|
|
362
|
+
const module = createGeneratorModule([
|
|
363
|
+
{
|
|
364
|
+
kind: "ifStatement",
|
|
365
|
+
condition: { kind: "literal", value: true },
|
|
366
|
+
thenStatement: {
|
|
367
|
+
kind: "blockStatement",
|
|
368
|
+
statements: [
|
|
369
|
+
{
|
|
370
|
+
kind: "expressionStatement",
|
|
371
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
]);
|
|
377
|
+
const result = runYieldLoweringPass([module]);
|
|
378
|
+
expect(result.ok).to.be.true;
|
|
379
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
380
|
+
const ifStmt = body[0];
|
|
381
|
+
const thenBlock = ifStmt.thenStatement;
|
|
382
|
+
expect(thenBlock.statements[0]?.kind).to.equal("yieldStatement");
|
|
383
|
+
});
|
|
384
|
+
it("should transform yield inside while loop", () => {
|
|
385
|
+
const module = createGeneratorModule([
|
|
386
|
+
{
|
|
387
|
+
kind: "whileStatement",
|
|
388
|
+
condition: { kind: "literal", value: true },
|
|
389
|
+
body: {
|
|
390
|
+
kind: "blockStatement",
|
|
391
|
+
statements: [
|
|
392
|
+
{
|
|
393
|
+
kind: "expressionStatement",
|
|
394
|
+
expression: createYield({ kind: "identifier", name: "i" }),
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
]);
|
|
400
|
+
const result = runYieldLoweringPass([module]);
|
|
401
|
+
expect(result.ok).to.be.true;
|
|
402
|
+
});
|
|
403
|
+
it("should transform yield inside switch case", () => {
|
|
404
|
+
const module = createGeneratorModule([
|
|
405
|
+
{
|
|
406
|
+
kind: "switchStatement",
|
|
407
|
+
expression: { kind: "identifier", name: "x" },
|
|
408
|
+
cases: [
|
|
409
|
+
{
|
|
410
|
+
kind: "switchCase",
|
|
411
|
+
test: { kind: "literal", value: 1 },
|
|
412
|
+
statements: [
|
|
413
|
+
{
|
|
414
|
+
kind: "expressionStatement",
|
|
415
|
+
expression: createYield({ kind: "literal", value: 10 }),
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
},
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
]);
|
|
422
|
+
const result = runYieldLoweringPass([module]);
|
|
423
|
+
expect(result.ok).to.be.true;
|
|
424
|
+
});
|
|
425
|
+
it("should transform yield inside try block", () => {
|
|
426
|
+
const module = createGeneratorModule([
|
|
427
|
+
{
|
|
428
|
+
kind: "tryStatement",
|
|
429
|
+
tryBlock: {
|
|
430
|
+
kind: "blockStatement",
|
|
431
|
+
statements: [
|
|
432
|
+
{
|
|
433
|
+
kind: "expressionStatement",
|
|
434
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
]);
|
|
440
|
+
const result = runYieldLoweringPass([module]);
|
|
441
|
+
expect(result.ok).to.be.true;
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
describe("Non-Generator Functions", () => {
|
|
445
|
+
it("should not transform yield expressions in non-generator functions", () => {
|
|
446
|
+
// This shouldn't happen in practice (TS would reject it),
|
|
447
|
+
// but the pass should handle it gracefully
|
|
448
|
+
const module = createGeneratorModule([
|
|
449
|
+
{
|
|
450
|
+
kind: "expressionStatement",
|
|
451
|
+
expression: createYield({ kind: "literal", value: 42 }),
|
|
452
|
+
},
|
|
453
|
+
], { isGenerator: false });
|
|
454
|
+
const result = runYieldLoweringPass([module]);
|
|
455
|
+
// The pass doesn't transform non-generators
|
|
456
|
+
expect(result.ok).to.be.true;
|
|
457
|
+
const body = getGeneratorBody(result.modules[0]);
|
|
458
|
+
// Should still be expressionStatement with yield, not yieldStatement
|
|
459
|
+
expect(body[0]?.kind).to.equal("expressionStatement");
|
|
460
|
+
});
|
|
461
|
+
it("should process nested generator functions inside non-generators", () => {
|
|
462
|
+
const module = {
|
|
463
|
+
kind: "module",
|
|
464
|
+
filePath: "/src/test.ts",
|
|
465
|
+
namespace: "Test",
|
|
466
|
+
className: "test",
|
|
467
|
+
isStaticContainer: true,
|
|
468
|
+
imports: [],
|
|
469
|
+
body: [
|
|
470
|
+
{
|
|
471
|
+
kind: "functionDeclaration",
|
|
472
|
+
name: "outer",
|
|
473
|
+
parameters: [],
|
|
474
|
+
returnType: { kind: "voidType" },
|
|
475
|
+
body: {
|
|
476
|
+
kind: "blockStatement",
|
|
477
|
+
statements: [
|
|
478
|
+
{
|
|
479
|
+
kind: "functionDeclaration",
|
|
480
|
+
name: "innerGen",
|
|
481
|
+
parameters: [],
|
|
482
|
+
returnType: {
|
|
483
|
+
kind: "referenceType",
|
|
484
|
+
name: "Generator",
|
|
485
|
+
typeArguments: [{ kind: "primitiveType", name: "number" }],
|
|
486
|
+
},
|
|
487
|
+
body: {
|
|
488
|
+
kind: "blockStatement",
|
|
489
|
+
statements: [
|
|
490
|
+
{
|
|
491
|
+
kind: "expressionStatement",
|
|
492
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
},
|
|
496
|
+
isAsync: false,
|
|
497
|
+
isGenerator: true,
|
|
498
|
+
isExported: false,
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
},
|
|
502
|
+
isAsync: false,
|
|
503
|
+
isGenerator: false,
|
|
504
|
+
isExported: true,
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
exports: [],
|
|
508
|
+
};
|
|
509
|
+
const result = runYieldLoweringPass([module]);
|
|
510
|
+
expect(result.ok).to.be.true;
|
|
511
|
+
// The inner generator should have its yield transformed
|
|
512
|
+
const outerFunc = result.modules[0]?.body[0];
|
|
513
|
+
const innerFunc = outerFunc.body.statements[0];
|
|
514
|
+
expect(innerFunc.body.statements[0]?.kind).to.equal("yieldStatement");
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
describe("Multiple Modules", () => {
|
|
518
|
+
it("should process multiple modules independently", () => {
|
|
519
|
+
const module1 = createGeneratorModule([
|
|
520
|
+
{
|
|
521
|
+
kind: "expressionStatement",
|
|
522
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
523
|
+
},
|
|
524
|
+
]);
|
|
525
|
+
const module2 = createGeneratorModule([
|
|
526
|
+
{
|
|
527
|
+
kind: "expressionStatement",
|
|
528
|
+
expression: createYield({ kind: "literal", value: 2 }),
|
|
529
|
+
},
|
|
530
|
+
]);
|
|
531
|
+
const result = runYieldLoweringPass([module1, module2]);
|
|
532
|
+
expect(result.ok).to.be.true;
|
|
533
|
+
expect(result.modules).to.have.length(2);
|
|
534
|
+
const body1 = getGeneratorBody(result.modules[0]);
|
|
535
|
+
const body2 = getGeneratorBody(result.modules[1]);
|
|
536
|
+
expect(body1[0]?.kind).to.equal("yieldStatement");
|
|
537
|
+
expect(body2[0]?.kind).to.equal("yieldStatement");
|
|
538
|
+
});
|
|
539
|
+
it("should collect diagnostics from all modules", () => {
|
|
540
|
+
const module1 = createGeneratorModule([
|
|
541
|
+
{
|
|
542
|
+
kind: "returnStatement",
|
|
543
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
544
|
+
},
|
|
545
|
+
]);
|
|
546
|
+
const module2 = createGeneratorModule([
|
|
547
|
+
{
|
|
548
|
+
kind: "throwStatement",
|
|
549
|
+
expression: createYield({ kind: "identifier", name: "err" }),
|
|
550
|
+
},
|
|
551
|
+
]);
|
|
552
|
+
const result = runYieldLoweringPass([module1, module2]);
|
|
553
|
+
expect(result.ok).to.be.false;
|
|
554
|
+
expect(result.diagnostics).to.have.length(2);
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
describe("Pipeline Contract: No Yield Expressions After Lowering", () => {
|
|
558
|
+
/**
|
|
559
|
+
* Helper to recursively check if any IrYieldExpression nodes remain in the IR
|
|
560
|
+
* Uses a simple approach that doesn't require exhaustive type coverage
|
|
561
|
+
*/
|
|
562
|
+
const containsYieldExpression = (node) => {
|
|
563
|
+
if (!node || typeof node !== "object")
|
|
564
|
+
return false;
|
|
565
|
+
const obj = node;
|
|
566
|
+
// Check for yield expression directly
|
|
567
|
+
if (obj.kind === "yield") {
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
// Check common container fields
|
|
571
|
+
if (obj.statements && Array.isArray(obj.statements)) {
|
|
572
|
+
return obj.statements.some(containsYieldExpression);
|
|
573
|
+
}
|
|
574
|
+
if (obj.expression) {
|
|
575
|
+
return containsYieldExpression(obj.expression);
|
|
576
|
+
}
|
|
577
|
+
if (obj.declarations && Array.isArray(obj.declarations)) {
|
|
578
|
+
return obj.declarations.some((d) => containsYieldExpression(d.initializer));
|
|
579
|
+
}
|
|
580
|
+
if (obj.body) {
|
|
581
|
+
return containsYieldExpression(obj.body);
|
|
582
|
+
}
|
|
583
|
+
if (obj.condition) {
|
|
584
|
+
if (containsYieldExpression(obj.condition))
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
if (obj.consequent) {
|
|
588
|
+
if (containsYieldExpression(obj.consequent))
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
if (obj.alternate) {
|
|
592
|
+
if (containsYieldExpression(obj.alternate))
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
595
|
+
if (obj.left) {
|
|
596
|
+
if (containsYieldExpression(obj.left))
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
if (obj.right) {
|
|
600
|
+
if (containsYieldExpression(obj.right))
|
|
601
|
+
return true;
|
|
602
|
+
}
|
|
603
|
+
if (obj.arguments && Array.isArray(obj.arguments)) {
|
|
604
|
+
if (obj.arguments.some(containsYieldExpression))
|
|
605
|
+
return true;
|
|
606
|
+
}
|
|
607
|
+
if (obj.callee) {
|
|
608
|
+
if (containsYieldExpression(obj.callee))
|
|
609
|
+
return true;
|
|
610
|
+
}
|
|
611
|
+
return false;
|
|
612
|
+
};
|
|
613
|
+
it("should transform ALL yield expressions in generator function body", () => {
|
|
614
|
+
const module = createGeneratorModule([
|
|
615
|
+
// Multiple yield patterns
|
|
616
|
+
{
|
|
617
|
+
kind: "expressionStatement",
|
|
618
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
kind: "variableDeclaration",
|
|
622
|
+
declarationKind: "const",
|
|
623
|
+
isExported: false,
|
|
624
|
+
declarations: [
|
|
625
|
+
{
|
|
626
|
+
kind: "variableDeclarator",
|
|
627
|
+
name: { kind: "identifierPattern", name: "x" },
|
|
628
|
+
initializer: createYield({ kind: "literal", value: 2 }),
|
|
629
|
+
},
|
|
630
|
+
],
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
kind: "whileStatement",
|
|
634
|
+
condition: { kind: "literal", value: true },
|
|
635
|
+
body: {
|
|
636
|
+
kind: "blockStatement",
|
|
637
|
+
statements: [
|
|
638
|
+
{
|
|
639
|
+
kind: "expressionStatement",
|
|
640
|
+
expression: createYield({ kind: "literal", value: 3 }),
|
|
641
|
+
},
|
|
642
|
+
],
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
]);
|
|
646
|
+
const result = runYieldLoweringPass([module]);
|
|
647
|
+
expect(result.ok).to.be.true;
|
|
648
|
+
// Verify no yield expressions remain
|
|
649
|
+
const func = result.modules[0]?.body[0];
|
|
650
|
+
const hasYieldExpr = containsYieldExpression(func.body);
|
|
651
|
+
expect(hasYieldExpr).to.be.false;
|
|
652
|
+
});
|
|
653
|
+
it("should leave yield expressions in non-generator functions unchanged", () => {
|
|
654
|
+
// Non-generator function with yield (would be invalid TS but pass handles gracefully)
|
|
655
|
+
const module = createGeneratorModule([
|
|
656
|
+
{
|
|
657
|
+
kind: "expressionStatement",
|
|
658
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
659
|
+
},
|
|
660
|
+
], { isGenerator: false });
|
|
661
|
+
const result = runYieldLoweringPass([module]);
|
|
662
|
+
expect(result.ok).to.be.true;
|
|
663
|
+
// Yield expression SHOULD remain in non-generator
|
|
664
|
+
const func = result.modules[0]?.body[0];
|
|
665
|
+
const hasYieldExpr = containsYieldExpression(func.body);
|
|
666
|
+
expect(hasYieldExpr).to.be.true;
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
describe("Generator Return Statement Transformation", () => {
|
|
670
|
+
it("should transform return statements in generators to generatorReturnStatement", () => {
|
|
671
|
+
const module = createGeneratorModule([
|
|
672
|
+
{
|
|
673
|
+
kind: "expressionStatement",
|
|
674
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
kind: "returnStatement",
|
|
678
|
+
expression: { kind: "literal", value: "done" },
|
|
679
|
+
},
|
|
680
|
+
]);
|
|
681
|
+
const result = runYieldLoweringPass([module]);
|
|
682
|
+
expect(result.ok).to.be.true;
|
|
683
|
+
const func = result.modules[0]?.body[0];
|
|
684
|
+
const body = func.body.statements;
|
|
685
|
+
// First statement should be yieldStatement
|
|
686
|
+
expect(body[0]?.kind).to.equal("yieldStatement");
|
|
687
|
+
// Second statement should be generatorReturnStatement
|
|
688
|
+
expect(body[1]?.kind).to.equal("generatorReturnStatement");
|
|
689
|
+
const genReturn = body[1];
|
|
690
|
+
expect(genReturn.expression?.kind).to.equal("literal");
|
|
691
|
+
expect(genReturn.expression.value).to.equal("done");
|
|
692
|
+
});
|
|
693
|
+
it("should transform bare return statements (no expression) to generatorReturnStatement", () => {
|
|
694
|
+
const module = createGeneratorModule([
|
|
695
|
+
{
|
|
696
|
+
kind: "expressionStatement",
|
|
697
|
+
expression: createYield({ kind: "literal", value: 1 }),
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
kind: "returnStatement",
|
|
701
|
+
expression: undefined,
|
|
702
|
+
},
|
|
703
|
+
]);
|
|
704
|
+
const result = runYieldLoweringPass([module]);
|
|
705
|
+
expect(result.ok).to.be.true;
|
|
706
|
+
const func = result.modules[0]?.body[0];
|
|
707
|
+
const body = func.body.statements;
|
|
708
|
+
// Second statement should be generatorReturnStatement with no expression
|
|
709
|
+
expect(body[1]?.kind).to.equal("generatorReturnStatement");
|
|
710
|
+
const genReturn = body[1];
|
|
711
|
+
expect(genReturn.expression).to.be.undefined;
|
|
712
|
+
});
|
|
713
|
+
it("should transform return statements inside nested control flow", () => {
|
|
714
|
+
const module = createGeneratorModule([
|
|
715
|
+
{
|
|
716
|
+
kind: "ifStatement",
|
|
717
|
+
condition: { kind: "identifier", name: "cond" },
|
|
718
|
+
thenStatement: {
|
|
719
|
+
kind: "blockStatement",
|
|
720
|
+
statements: [
|
|
721
|
+
{
|
|
722
|
+
kind: "returnStatement",
|
|
723
|
+
expression: { kind: "literal", value: "early" },
|
|
724
|
+
},
|
|
725
|
+
],
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
kind: "returnStatement",
|
|
730
|
+
expression: { kind: "literal", value: "done" },
|
|
731
|
+
},
|
|
732
|
+
]);
|
|
733
|
+
const result = runYieldLoweringPass([module]);
|
|
734
|
+
expect(result.ok).to.be.true;
|
|
735
|
+
const func = result.modules[0]?.body[0];
|
|
736
|
+
const body = func.body.statements;
|
|
737
|
+
// Check if statement then branch contains generatorReturnStatement
|
|
738
|
+
const ifStmt = body[0];
|
|
739
|
+
const thenBlock = ifStmt.thenStatement;
|
|
740
|
+
expect(thenBlock.statements[0]?.kind).to.equal("generatorReturnStatement");
|
|
741
|
+
// Final return also transformed
|
|
742
|
+
expect(body[1]?.kind).to.equal("generatorReturnStatement");
|
|
743
|
+
});
|
|
744
|
+
it("should NOT transform return statements in non-generator functions", () => {
|
|
745
|
+
// Non-generator function
|
|
746
|
+
const module = createGeneratorModule([
|
|
747
|
+
{
|
|
748
|
+
kind: "returnStatement",
|
|
749
|
+
expression: { kind: "literal", value: "result" },
|
|
750
|
+
},
|
|
751
|
+
], { isGenerator: false });
|
|
752
|
+
const result = runYieldLoweringPass([module]);
|
|
753
|
+
expect(result.ok).to.be.true;
|
|
754
|
+
const func = result.modules[0]?.body[0];
|
|
755
|
+
const body = func.body.statements;
|
|
756
|
+
// Return statement should remain as returnStatement (not transformed)
|
|
757
|
+
expect(body[0]?.kind).to.equal("returnStatement");
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
});
|
|
761
|
+
//# sourceMappingURL=yield-lowering-pass.test.js.map
|