@elaraai/east 1.0.96 → 1.0.98
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/src/compile/constructors.d.ts +6 -1
- package/dist/src/compile/constructors.d.ts.map +1 -1
- package/dist/src/compile/constructors.js +21 -11
- package/dist/src/compile/constructors.js.map +1 -1
- package/dist/src/compile/control.d.ts +7 -1
- package/dist/src/compile/control.d.ts.map +1 -1
- package/dist/src/compile/control.js +228 -146
- package/dist/src/compile/control.js.map +1 -1
- package/dist/src/compile/flow.d.ts +138 -0
- package/dist/src/compile/flow.d.ts.map +1 -0
- package/dist/src/compile/flow.js +358 -0
- package/dist/src/compile/flow.js.map +1 -0
- package/dist/src/compile/functions.d.ts +9 -1
- package/dist/src/compile/functions.d.ts.map +1 -1
- package/dist/src/compile/functions.js +62 -69
- package/dist/src/compile/functions.js.map +1 -1
- package/dist/src/compile/ir.d.ts +12 -0
- package/dist/src/compile/ir.d.ts.map +1 -1
- package/dist/src/compile/ir.js +164 -40
- package/dist/src/compile/ir.js.map +1 -1
- package/dist/src/compile/runtime.d.ts +2 -16
- package/dist/src/compile/runtime.d.ts.map +1 -1
- package/dist/src/compile/runtime.js +5 -40
- package/dist/src/compile/runtime.js.map +1 -1
- package/dist/src/compile.control_flow.spec.d.ts +6 -0
- package/dist/src/compile.control_flow.spec.d.ts.map +1 -0
- package/dist/src/compile.control_flow.spec.js +219 -0
- package/dist/src/compile.control_flow.spec.js.map +1 -0
- package/dist/src/compile.d.ts +1 -1
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +1 -1
- package/dist/src/compile.js.map +1 -1
- package/dist/src/eastir.d.ts.map +1 -1
- package/dist/src/eastir.js +8 -30
- package/dist/src/eastir.js.map +1 -1
- package/dist/src/internal.d.ts +1 -0
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +4 -0
- package/dist/src/internal.js.map +1 -1
- package/dist/src/serialization/beast2/shared.d.ts.map +1 -1
- package/dist/src/serialization/beast2/shared.js +6 -21
- package/dist/src/serialization/beast2/shared.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,32 +1,66 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { compile_ir } from "./ir.js";
|
|
2
|
+
import { asStatement, ControlSignal, escapesOf, loopReads, NO_ESCAPES } from "./flow.js";
|
|
3
|
+
import { lazyReadErrorAt, lockForIteration, unlockForIteration } from "./runtime.js";
|
|
3
4
|
import { variant } from "../containers/variant.js";
|
|
4
5
|
import { EastError } from "../error.js";
|
|
5
6
|
import { loopWalk } from "../serialization/beast2/v5/lazy.js";
|
|
6
7
|
import { expandTypeValue } from "../type_of_type.js";
|
|
7
|
-
/**
|
|
8
|
+
/** A child, compiled to run as a statement: a signal a node under it threw is given back. */
|
|
9
|
+
function statement(child, child_ctx, fresh_ctx, platform, asyncPlatformFns, platformDef, compilingNodes) {
|
|
10
|
+
return asStatement(child, compile_ir(child, child_ctx, platform, asyncPlatformFns, platformDef, fresh_ctx, compilingNodes));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Compiles the errors, try/catch, branches, loops, and early exits.
|
|
14
|
+
*
|
|
15
|
+
* Each runs its children as statements, and gives back a `return`, `continue`
|
|
16
|
+
* or `break` one of them gives — a loop acting on those that name it — so
|
|
17
|
+
* control flow is never thrown here (`flow.ts`).
|
|
18
|
+
*/
|
|
8
19
|
export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef, compilingNodes, source_map) {
|
|
9
20
|
if (ir.type === "Error") {
|
|
10
|
-
const message_compiled =
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
const message_compiled = statement(ir.value.message, ctx, false, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
22
|
+
const err_location = source_map?.resolve(ir.value.loc_id) ?? [];
|
|
23
|
+
if (escapesOf(ir.value.message) !== NO_ESCAPES) {
|
|
24
|
+
// A message a control statement leaves raises nothing: the signal leaves the error too.
|
|
25
|
+
if (ir.value.isAsync) {
|
|
26
|
+
return async (ctx) => {
|
|
27
|
+
const message = await message_compiled(ctx);
|
|
28
|
+
if (message instanceof ControlSignal)
|
|
29
|
+
return message;
|
|
30
|
+
throw new EastError(message, { location: err_location });
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return (ctx) => {
|
|
35
|
+
const message = message_compiled(ctx);
|
|
36
|
+
if (message instanceof ControlSignal)
|
|
37
|
+
return message;
|
|
38
|
+
throw new EastError(message, { location: err_location });
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (ir.value.isAsync) {
|
|
13
43
|
return async (ctx) => { throw new EastError(await message_compiled(ctx), { location: err_location }); };
|
|
14
44
|
}
|
|
15
45
|
else {
|
|
16
|
-
const err_location = source_map?.resolve(ir.value.loc_id) ?? [];
|
|
17
46
|
return (ctx) => { throw new EastError(message_compiled(ctx), { location: err_location }); };
|
|
18
47
|
}
|
|
19
48
|
}
|
|
20
49
|
else if (ir.type === "TryCatch") {
|
|
21
|
-
const try_body =
|
|
50
|
+
const try_body = statement(ir.value.try_body, Object.create(ctx), true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
22
51
|
const new_ctx = Object.create(ctx);
|
|
23
52
|
const message_name = ir.value.message.value.name;
|
|
24
53
|
const stack_name = ir.value.stack.value.name;
|
|
25
54
|
new_ctx[message_name] = ir.value.message.value.type;
|
|
26
55
|
new_ctx[stack_name] = ir.value.stack.value.type;
|
|
27
|
-
const catch_body =
|
|
56
|
+
const catch_body = statement(ir.value.catch_body, new_ctx, true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
28
57
|
// Don't include finally unless necessary (Value nodes are effect free)
|
|
29
|
-
const finally_body = ir.value.finally_body.type === "Value" ? undefined :
|
|
58
|
+
const finally_body = ir.value.finally_body.type === "Value" ? undefined : statement(ir.value.finally_body, Object.create(ctx), true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
59
|
+
// A signal the finally gives leaves in place of what the try or the catch
|
|
60
|
+
// gave, or threw, as a control statement thrown from the finally did.
|
|
61
|
+
const finally_leaves = finally_body !== undefined && escapesOf(ir.value.finally_body) !== NO_ESCAPES;
|
|
62
|
+
// The try and the catch give back a signal as their value, so East's catch
|
|
63
|
+
// never sees one: it catches East's errors alone.
|
|
30
64
|
if (ir.value.isAsync) {
|
|
31
65
|
if (finally_body === undefined) {
|
|
32
66
|
return async (ctx) => {
|
|
@@ -63,7 +97,9 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
63
97
|
}
|
|
64
98
|
}
|
|
65
99
|
finally {
|
|
66
|
-
await finally_body(Object.create(ctx));
|
|
100
|
+
const left = await finally_body(Object.create(ctx));
|
|
101
|
+
if (finally_leaves && left instanceof ControlSignal)
|
|
102
|
+
return left;
|
|
67
103
|
}
|
|
68
104
|
};
|
|
69
105
|
}
|
|
@@ -104,7 +140,9 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
104
140
|
}
|
|
105
141
|
}
|
|
106
142
|
finally {
|
|
107
|
-
finally_body(Object.create(ctx));
|
|
143
|
+
const left = finally_body(Object.create(ctx));
|
|
144
|
+
if (finally_leaves && left instanceof ControlSignal)
|
|
145
|
+
return left;
|
|
108
146
|
}
|
|
109
147
|
};
|
|
110
148
|
}
|
|
@@ -113,17 +151,50 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
113
151
|
else if (ir.type === "IfElse") {
|
|
114
152
|
const ifs = [];
|
|
115
153
|
let asyncPredicate = false;
|
|
154
|
+
let predicateLeaves = false;
|
|
116
155
|
for (const { predicate, body } of ir.value.ifs) {
|
|
117
156
|
if (predicate.value.isAsync) {
|
|
118
157
|
asyncPredicate = true;
|
|
119
158
|
}
|
|
159
|
+
if (escapesOf(predicate) !== NO_ESCAPES) {
|
|
160
|
+
predicateLeaves = true;
|
|
161
|
+
}
|
|
120
162
|
ifs.push({
|
|
121
|
-
predicate:
|
|
122
|
-
body:
|
|
163
|
+
predicate: statement(predicate, ctx, false, platform, asyncPlatformFns, platformDef, compilingNodes),
|
|
164
|
+
body: statement(body, Object.create(ctx), true, platform, asyncPlatformFns, platformDef, compilingNodes),
|
|
123
165
|
});
|
|
124
166
|
}
|
|
125
|
-
const else_body =
|
|
126
|
-
if (
|
|
167
|
+
const else_body = statement(ir.value.else_body, Object.create(ctx), true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
168
|
+
if (predicateLeaves) {
|
|
169
|
+
// A predicate a control statement leaves gives its signal in place of a branch's value.
|
|
170
|
+
if (ir.value.isAsync) {
|
|
171
|
+
return async (ctx) => {
|
|
172
|
+
for (const { predicate, body } of ifs) {
|
|
173
|
+
const p = await predicate(ctx);
|
|
174
|
+
if (p instanceof ControlSignal)
|
|
175
|
+
return p;
|
|
176
|
+
if (p) {
|
|
177
|
+
return await body(Object.create(ctx));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return await else_body(Object.create(ctx));
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
return (ctx) => {
|
|
185
|
+
for (const { predicate, body } of ifs) {
|
|
186
|
+
const p = predicate(ctx);
|
|
187
|
+
if (p instanceof ControlSignal)
|
|
188
|
+
return p;
|
|
189
|
+
if (p) {
|
|
190
|
+
return body(Object.create(ctx));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return else_body(Object.create(ctx));
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else if (ir.value.isAsync) {
|
|
127
198
|
if (asyncPredicate) {
|
|
128
199
|
return async (ctx) => {
|
|
129
200
|
for (const { predicate, body } of ifs) {
|
|
@@ -157,7 +228,7 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
157
228
|
}
|
|
158
229
|
}
|
|
159
230
|
else if (ir.type === "Match") {
|
|
160
|
-
const compiled_variant =
|
|
231
|
+
const compiled_variant = statement(ir.value.variant, ctx, false, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
161
232
|
const compiled_cases = {};
|
|
162
233
|
const data_names = {};
|
|
163
234
|
for (const { case: k, variable, body } of ir.value.cases) {
|
|
@@ -165,9 +236,32 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
165
236
|
const data_name = variable.value.name;
|
|
166
237
|
data_names[k] = data_name;
|
|
167
238
|
ctx2[data_name] = variable.value.type;
|
|
168
|
-
compiled_cases[k] =
|
|
239
|
+
compiled_cases[k] = statement(body, ctx2, true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
169
240
|
}
|
|
170
|
-
if (ir.value.
|
|
241
|
+
if (escapesOf(ir.value.variant) !== NO_ESCAPES) {
|
|
242
|
+
// A variant a control statement leaves gives its signal in place of a case's value.
|
|
243
|
+
if (ir.value.isAsync) {
|
|
244
|
+
return async (ctx) => {
|
|
245
|
+
const v = await compiled_variant(ctx);
|
|
246
|
+
if (v instanceof ControlSignal)
|
|
247
|
+
return v;
|
|
248
|
+
const ctx2 = Object.create(ctx);
|
|
249
|
+
ctx2[data_names[v.type]] = variant("value", v.value);
|
|
250
|
+
return await compiled_cases[v.type](ctx2);
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
return (ctx) => {
|
|
255
|
+
const v = compiled_variant(ctx);
|
|
256
|
+
if (v instanceof ControlSignal)
|
|
257
|
+
return v;
|
|
258
|
+
const ctx2 = Object.create(ctx);
|
|
259
|
+
ctx2[data_names[v.type]] = variant("value", v.value);
|
|
260
|
+
return compiled_cases[v.type](ctx2);
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
else if (ir.value.isAsync) {
|
|
171
265
|
if (ir.value.variant.value.isAsync) {
|
|
172
266
|
return async (ctx) => {
|
|
173
267
|
const v = await compiled_variant(ctx);
|
|
@@ -195,88 +289,85 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
195
289
|
}
|
|
196
290
|
}
|
|
197
291
|
else if (ir.type === "While") {
|
|
198
|
-
const compiled_predicate =
|
|
292
|
+
const compiled_predicate = statement(ir.value.predicate, ctx, false, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
199
293
|
const ctx2 = Object.create(ctx);
|
|
200
|
-
const compiled_body =
|
|
294
|
+
const compiled_body = statement(ir.value.body, ctx2, true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
201
295
|
const label = ir.value.label.name;
|
|
296
|
+
// Whether the predicate can give a signal, which leaves the loop; and
|
|
297
|
+
// whether the body can give one but its own `continue`, which the loop
|
|
298
|
+
// must act on: its own `break` stops it, and any other leaves it.
|
|
299
|
+
const predicate_leaves = escapesOf(ir.value.predicate) !== NO_ESCAPES;
|
|
300
|
+
const reads = loopReads(ir.value.body, label);
|
|
202
301
|
if (ir.value.isAsync) {
|
|
203
302
|
return async (ctx) => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
throw e;
|
|
218
|
-
}
|
|
303
|
+
for (;;) {
|
|
304
|
+
const p = await compiled_predicate(ctx);
|
|
305
|
+
if (predicate_leaves && p instanceof ControlSignal)
|
|
306
|
+
return p;
|
|
307
|
+
if (!p)
|
|
308
|
+
return null;
|
|
309
|
+
const ctx2 = Object.create(ctx);
|
|
310
|
+
const r = await compiled_body(ctx2);
|
|
311
|
+
if (reads && r instanceof ControlSignal) {
|
|
312
|
+
if (r.kind === "return" || r.label !== label)
|
|
313
|
+
return r;
|
|
314
|
+
if (r.kind === "break")
|
|
315
|
+
return null;
|
|
219
316
|
}
|
|
220
317
|
}
|
|
221
|
-
return null;
|
|
222
318
|
};
|
|
223
319
|
}
|
|
224
320
|
else {
|
|
225
321
|
return (ctx) => {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
throw e;
|
|
240
|
-
}
|
|
322
|
+
for (;;) {
|
|
323
|
+
const p = compiled_predicate(ctx);
|
|
324
|
+
if (predicate_leaves && p instanceof ControlSignal)
|
|
325
|
+
return p;
|
|
326
|
+
if (!p)
|
|
327
|
+
return null;
|
|
328
|
+
const ctx2 = Object.create(ctx);
|
|
329
|
+
const r = compiled_body(ctx2);
|
|
330
|
+
if (reads && r instanceof ControlSignal) {
|
|
331
|
+
if (r.kind === "return" || r.label !== label)
|
|
332
|
+
return r;
|
|
333
|
+
if (r.kind === "break")
|
|
334
|
+
return null;
|
|
241
335
|
}
|
|
242
336
|
}
|
|
243
|
-
return null;
|
|
244
337
|
};
|
|
245
338
|
}
|
|
246
339
|
}
|
|
247
340
|
else if (ir.type === "ForArray") {
|
|
248
341
|
const value_type = expandTypeValue(ir.value.array.value.type).value;
|
|
249
|
-
const compiled_array =
|
|
342
|
+
const compiled_array = statement(ir.value.array, ctx, false, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
250
343
|
const ctx2 = Object.create(ctx);
|
|
251
344
|
const key_name = ir.value.key.value.name;
|
|
252
345
|
const value_name = ir.value.value.value.name;
|
|
253
346
|
ctx2[key_name] = variant("Integer", null);
|
|
254
347
|
ctx2[value_name] = value_type;
|
|
255
|
-
const compiled_body =
|
|
348
|
+
const compiled_body = statement(ir.value.body, ctx2, true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
256
349
|
const label = ir.value.label.name;
|
|
257
350
|
const loc_id = ir.value.loc_id;
|
|
351
|
+
// As a While's: whether the array can give a signal, and whether the loop must act on what its body gives.
|
|
352
|
+
const array_leaves = escapesOf(ir.value.array) !== NO_ESCAPES;
|
|
353
|
+
const reads = loopReads(ir.value.body, label);
|
|
258
354
|
if (ir.value.isAsync) {
|
|
259
355
|
return async (ctx) => {
|
|
260
356
|
const array = await compiled_array(ctx);
|
|
357
|
+
if (array_leaves && array instanceof ControlSignal)
|
|
358
|
+
return array;
|
|
261
359
|
lockForIteration(array);
|
|
262
360
|
try {
|
|
263
361
|
for (const [key, value] of array.entries()) {
|
|
264
362
|
const ctx2 = Object.create(ctx);
|
|
265
363
|
ctx2[key_name] = variant("value", BigInt(key));
|
|
266
364
|
ctx2[value_name] = variant("value", value);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
else if (e instanceof BreakException && e.label === label) {
|
|
365
|
+
const r = await compiled_body(ctx2);
|
|
366
|
+
if (reads && r instanceof ControlSignal) {
|
|
367
|
+
if (r.kind === "return" || r.label !== label)
|
|
368
|
+
return r;
|
|
369
|
+
if (r.kind === "break")
|
|
275
370
|
break;
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
throw e;
|
|
279
|
-
}
|
|
280
371
|
}
|
|
281
372
|
}
|
|
282
373
|
return null;
|
|
@@ -292,25 +383,20 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
292
383
|
else {
|
|
293
384
|
return (ctx) => {
|
|
294
385
|
const array = compiled_array(ctx);
|
|
386
|
+
if (array_leaves && array instanceof ControlSignal)
|
|
387
|
+
return array;
|
|
295
388
|
lockForIteration(array);
|
|
296
389
|
try {
|
|
297
390
|
for (const [key, value] of array.entries()) {
|
|
298
391
|
const ctx2 = Object.create(ctx);
|
|
299
392
|
ctx2[key_name] = variant("value", BigInt(key));
|
|
300
393
|
ctx2[value_name] = variant("value", value);
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (
|
|
306
|
-
continue;
|
|
307
|
-
}
|
|
308
|
-
else if (e instanceof BreakException && e.label === label) {
|
|
394
|
+
const r = compiled_body(ctx2);
|
|
395
|
+
if (reads && r instanceof ControlSignal) {
|
|
396
|
+
if (r.kind === "return" || r.label !== label)
|
|
397
|
+
return r;
|
|
398
|
+
if (r.kind === "break")
|
|
309
399
|
break;
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
throw e;
|
|
313
|
-
}
|
|
314
400
|
}
|
|
315
401
|
}
|
|
316
402
|
return null;
|
|
@@ -326,34 +412,32 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
326
412
|
}
|
|
327
413
|
else if (ir.type === "ForSet") {
|
|
328
414
|
const key_type = expandTypeValue(ir.value.set.value.type).value;
|
|
329
|
-
const compiled_set =
|
|
415
|
+
const compiled_set = statement(ir.value.set, ctx, false, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
330
416
|
const ctx2 = Object.create(ctx);
|
|
331
417
|
const key_name = ir.value.key.value.name;
|
|
332
418
|
ctx2[key_name] = key_type;
|
|
333
|
-
const compiled_body =
|
|
419
|
+
const compiled_body = statement(ir.value.body, ctx2, true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
334
420
|
const label = ir.value.label.name;
|
|
335
421
|
const loc_id = ir.value.loc_id;
|
|
422
|
+
// As a While's: whether the set can give a signal, and whether the loop must act on what its body gives.
|
|
423
|
+
const set_leaves = escapesOf(ir.value.set) !== NO_ESCAPES;
|
|
424
|
+
const reads = loopReads(ir.value.body, label);
|
|
336
425
|
if (ir.value.isAsync) {
|
|
337
426
|
return async (ctx) => {
|
|
338
427
|
const set = await compiled_set(ctx);
|
|
428
|
+
if (set_leaves && set instanceof ControlSignal)
|
|
429
|
+
return set;
|
|
339
430
|
lockForIteration(set);
|
|
340
431
|
try {
|
|
341
432
|
for (const key of loopWalk(set)) {
|
|
342
433
|
const ctx2 = Object.create(ctx);
|
|
343
434
|
ctx2[key_name] = variant("value", key);
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (
|
|
349
|
-
continue;
|
|
350
|
-
}
|
|
351
|
-
else if (e instanceof BreakException && e.label === label) {
|
|
435
|
+
const r = await compiled_body(ctx2);
|
|
436
|
+
if (reads && r instanceof ControlSignal) {
|
|
437
|
+
if (r.kind === "return" || r.label !== label)
|
|
438
|
+
return r;
|
|
439
|
+
if (r.kind === "break")
|
|
352
440
|
break;
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
throw e;
|
|
356
|
-
}
|
|
357
441
|
}
|
|
358
442
|
}
|
|
359
443
|
return null;
|
|
@@ -369,24 +453,19 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
369
453
|
else {
|
|
370
454
|
return (ctx) => {
|
|
371
455
|
const set = compiled_set(ctx);
|
|
456
|
+
if (set_leaves && set instanceof ControlSignal)
|
|
457
|
+
return set;
|
|
372
458
|
lockForIteration(set);
|
|
373
459
|
try {
|
|
374
460
|
for (const key of loopWalk(set)) {
|
|
375
461
|
const ctx2 = Object.create(ctx);
|
|
376
462
|
ctx2[key_name] = variant("value", key);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (
|
|
382
|
-
continue;
|
|
383
|
-
}
|
|
384
|
-
else if (e instanceof BreakException && e.label === label) {
|
|
463
|
+
const r = compiled_body(ctx2);
|
|
464
|
+
if (reads && r instanceof ControlSignal) {
|
|
465
|
+
if (r.kind === "return" || r.label !== label)
|
|
466
|
+
return r;
|
|
467
|
+
if (r.kind === "break")
|
|
385
468
|
break;
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
throw e;
|
|
389
|
-
}
|
|
390
469
|
}
|
|
391
470
|
}
|
|
392
471
|
return null;
|
|
@@ -403,37 +482,35 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
403
482
|
else if (ir.type === "ForDict") {
|
|
404
483
|
const key_type = expandTypeValue(ir.value.dict.value.type).value.key;
|
|
405
484
|
const value_type = expandTypeValue(ir.value.dict.value.type).value.value;
|
|
406
|
-
const compiled_dict =
|
|
485
|
+
const compiled_dict = statement(ir.value.dict, ctx, false, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
407
486
|
const ctx2 = Object.create(ctx);
|
|
408
487
|
const key_name = ir.value.key.value.name;
|
|
409
488
|
const value_name = ir.value.value.value.name;
|
|
410
489
|
ctx2[key_name] = key_type;
|
|
411
490
|
ctx2[value_name] = value_type;
|
|
412
|
-
const compiled_body =
|
|
491
|
+
const compiled_body = statement(ir.value.body, ctx2, true, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
413
492
|
const label = ir.value.label.name;
|
|
414
493
|
const loc_id = ir.value.loc_id;
|
|
494
|
+
// As a While's: whether the dict can give a signal, and whether the loop must act on what its body gives.
|
|
495
|
+
const dict_leaves = escapesOf(ir.value.dict) !== NO_ESCAPES;
|
|
496
|
+
const reads = loopReads(ir.value.body, label);
|
|
415
497
|
if (ir.value.isAsync) {
|
|
416
498
|
return async (ctx) => {
|
|
417
499
|
const dict = await compiled_dict(ctx);
|
|
500
|
+
if (dict_leaves && dict instanceof ControlSignal)
|
|
501
|
+
return dict;
|
|
418
502
|
lockForIteration(dict);
|
|
419
503
|
try {
|
|
420
504
|
for (const [key, value] of loopWalk(dict)) {
|
|
421
505
|
const ctx2 = Object.create(ctx);
|
|
422
506
|
ctx2[key_name] = variant("value", key);
|
|
423
507
|
ctx2[value_name] = variant("value", value);
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
431
|
-
else if (e instanceof BreakException && e.label === label) {
|
|
508
|
+
const r = await compiled_body(ctx2);
|
|
509
|
+
if (reads && r instanceof ControlSignal) {
|
|
510
|
+
if (r.kind === "return" || r.label !== label)
|
|
511
|
+
return r;
|
|
512
|
+
if (r.kind === "break")
|
|
432
513
|
break;
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
throw e;
|
|
436
|
-
}
|
|
437
514
|
}
|
|
438
515
|
}
|
|
439
516
|
return null;
|
|
@@ -449,25 +526,20 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
449
526
|
else {
|
|
450
527
|
return (ctx) => {
|
|
451
528
|
const dict = compiled_dict(ctx);
|
|
529
|
+
if (dict_leaves && dict instanceof ControlSignal)
|
|
530
|
+
return dict;
|
|
452
531
|
lockForIteration(dict);
|
|
453
532
|
try {
|
|
454
533
|
for (const [key, value] of loopWalk(dict)) {
|
|
455
534
|
const ctx2 = Object.create(ctx);
|
|
456
535
|
ctx2[key_name] = variant("value", key);
|
|
457
536
|
ctx2[value_name] = variant("value", value);
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
else if (e instanceof BreakException && e.label === label) {
|
|
537
|
+
const r = compiled_body(ctx2);
|
|
538
|
+
if (reads && r instanceof ControlSignal) {
|
|
539
|
+
if (r.kind === "return" || r.label !== label)
|
|
540
|
+
return r;
|
|
541
|
+
if (r.kind === "break")
|
|
466
542
|
break;
|
|
467
|
-
}
|
|
468
|
-
else {
|
|
469
|
-
throw e;
|
|
470
|
-
}
|
|
471
543
|
}
|
|
472
544
|
}
|
|
473
545
|
return null;
|
|
@@ -482,29 +554,39 @@ export function compile_control(ir, ctx, platform, asyncPlatformFns, platformDef
|
|
|
482
554
|
}
|
|
483
555
|
}
|
|
484
556
|
else if (ir.type === "Return") {
|
|
485
|
-
const compiled_value =
|
|
557
|
+
const compiled_value = statement(ir.value.value, ctx, false, platform, asyncPlatformFns, platformDef, compilingNodes);
|
|
558
|
+
// A value a control statement leaves gives its signal in place of the return's.
|
|
559
|
+
const value_leaves = escapesOf(ir.value.value) !== NO_ESCAPES;
|
|
486
560
|
if (ir.value.isAsync) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
561
|
+
if (value_leaves) {
|
|
562
|
+
return async (ctx) => {
|
|
563
|
+
const value = await compiled_value(ctx);
|
|
564
|
+
return value instanceof ControlSignal ? value : new ControlSignal("return", "", value);
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
return async (ctx) => new ControlSignal("return", "", await compiled_value(ctx));
|
|
569
|
+
}
|
|
490
570
|
}
|
|
491
571
|
else {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
572
|
+
if (value_leaves) {
|
|
573
|
+
return (ctx) => {
|
|
574
|
+
const value = compiled_value(ctx);
|
|
575
|
+
return value instanceof ControlSignal ? value : new ControlSignal("return", "", value);
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
return (ctx) => new ControlSignal("return", "", compiled_value(ctx));
|
|
580
|
+
}
|
|
495
581
|
}
|
|
496
582
|
}
|
|
497
583
|
else if (ir.type === "Continue") {
|
|
498
|
-
const
|
|
499
|
-
return (_ctx) =>
|
|
500
|
-
throw new ContinueException(label.name);
|
|
501
|
-
};
|
|
584
|
+
const signal = new ControlSignal("continue", ir.value.label.name, null);
|
|
585
|
+
return (_ctx) => signal;
|
|
502
586
|
}
|
|
503
587
|
else if (ir.type === "Break") {
|
|
504
|
-
const
|
|
505
|
-
return (_ctx) =>
|
|
506
|
-
throw new BreakException(label.name);
|
|
507
|
-
};
|
|
588
|
+
const signal = new ControlSignal("break", ir.value.label.name, null);
|
|
589
|
+
return (_ctx) => signal;
|
|
508
590
|
}
|
|
509
591
|
else {
|
|
510
592
|
throw new Error(`Unhandled IR type ${ir.type} at loc_id ${ir.value.loc_id}`); // The `satisfies never` here ensures that this branch is unreachable if all IR types are handled
|