@elaraai/east 1.0.97 → 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.
Files changed (43) hide show
  1. package/dist/src/compile/constructors.d.ts +6 -1
  2. package/dist/src/compile/constructors.d.ts.map +1 -1
  3. package/dist/src/compile/constructors.js +21 -11
  4. package/dist/src/compile/constructors.js.map +1 -1
  5. package/dist/src/compile/control.d.ts +7 -1
  6. package/dist/src/compile/control.d.ts.map +1 -1
  7. package/dist/src/compile/control.js +228 -146
  8. package/dist/src/compile/control.js.map +1 -1
  9. package/dist/src/compile/flow.d.ts +138 -0
  10. package/dist/src/compile/flow.d.ts.map +1 -0
  11. package/dist/src/compile/flow.js +358 -0
  12. package/dist/src/compile/flow.js.map +1 -0
  13. package/dist/src/compile/functions.d.ts +9 -1
  14. package/dist/src/compile/functions.d.ts.map +1 -1
  15. package/dist/src/compile/functions.js +62 -69
  16. package/dist/src/compile/functions.js.map +1 -1
  17. package/dist/src/compile/ir.d.ts +12 -0
  18. package/dist/src/compile/ir.d.ts.map +1 -1
  19. package/dist/src/compile/ir.js +164 -40
  20. package/dist/src/compile/ir.js.map +1 -1
  21. package/dist/src/compile/runtime.d.ts +2 -16
  22. package/dist/src/compile/runtime.d.ts.map +1 -1
  23. package/dist/src/compile/runtime.js +5 -40
  24. package/dist/src/compile/runtime.js.map +1 -1
  25. package/dist/src/compile.control_flow.spec.d.ts +6 -0
  26. package/dist/src/compile.control_flow.spec.d.ts.map +1 -0
  27. package/dist/src/compile.control_flow.spec.js +219 -0
  28. package/dist/src/compile.control_flow.spec.js.map +1 -0
  29. package/dist/src/compile.d.ts +1 -1
  30. package/dist/src/compile.d.ts.map +1 -1
  31. package/dist/src/compile.js +1 -1
  32. package/dist/src/compile.js.map +1 -1
  33. package/dist/src/eastir.d.ts.map +1 -1
  34. package/dist/src/eastir.js +8 -30
  35. package/dist/src/eastir.js.map +1 -1
  36. package/dist/src/internal.d.ts +1 -0
  37. package/dist/src/internal.d.ts.map +1 -1
  38. package/dist/src/internal.js +4 -0
  39. package/dist/src/internal.js.map +1 -1
  40. package/dist/src/serialization/beast2/shared.d.ts.map +1 -1
  41. package/dist/src/serialization/beast2/shared.js +6 -21
  42. package/dist/src/serialization/beast2/shared.js.map +1 -1
  43. package/package.json +1 -1
@@ -1,32 +1,66 @@
1
- import { compile_internal } from "./ir.js";
2
- import { BreakException, ContinueException, lazyReadErrorAt, lockForIteration, ReturnException, unlockForIteration } from "./runtime.js";
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
- /** Compiles the errors, try/catch, branches, loops, and early exits. */
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 = compile_internal(ir.value.message, ctx, platform, asyncPlatformFns, platformDef, false, compilingNodes);
11
- if (ir.value.isAsync) {
12
- const err_location = source_map?.resolve(ir.value.loc_id) ?? [];
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 = compile_internal(ir.value.try_body, Object.create(ctx), platform, asyncPlatformFns, platformDef, true, compilingNodes);
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 = compile_internal(ir.value.catch_body, new_ctx, platform, asyncPlatformFns, platformDef, true, compilingNodes);
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 : compile_internal(ir.value.finally_body, Object.create(ctx), platform, asyncPlatformFns, platformDef, true, compilingNodes);
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: compile_internal(predicate, ctx, platform, asyncPlatformFns, platformDef, false, compilingNodes),
122
- body: compile_internal(body, Object.create(ctx), platform, asyncPlatformFns, platformDef, true, compilingNodes),
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 = compile_internal(ir.value.else_body, Object.create(ctx), platform, asyncPlatformFns, platformDef, true, compilingNodes);
126
- if (ir.value.isAsync) {
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 = compile_internal(ir.value.variant, ctx, platform, asyncPlatformFns, platformDef, false, compilingNodes);
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] = compile_internal(body, ctx2, platform, asyncPlatformFns, platformDef, true, compilingNodes);
239
+ compiled_cases[k] = statement(body, ctx2, true, platform, asyncPlatformFns, platformDef, compilingNodes);
169
240
  }
170
- if (ir.value.isAsync) {
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 = compile_internal(ir.value.predicate, ctx, platform, asyncPlatformFns, platformDef, false, compilingNodes);
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 = compile_internal(ir.value.body, ctx2, platform, asyncPlatformFns, platformDef, true, compilingNodes);
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
- while (await compiled_predicate(ctx)) {
205
- try {
206
- const ctx2 = Object.create(ctx);
207
- await compiled_body(ctx2);
208
- }
209
- catch (e) {
210
- if (e instanceof ContinueException && e.label === label) {
211
- continue;
212
- }
213
- else if (e instanceof BreakException && e.label === label) {
214
- break;
215
- }
216
- else {
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
- while (compiled_predicate(ctx)) {
227
- try {
228
- const ctx2 = Object.create(ctx);
229
- compiled_body(ctx2);
230
- }
231
- catch (e) {
232
- if (e instanceof ContinueException && e.label === label) {
233
- continue;
234
- }
235
- else if (e instanceof BreakException && e.label === label) {
236
- break;
237
- }
238
- else {
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 = compile_internal(ir.value.array, ctx, platform, asyncPlatformFns, platformDef, false, compilingNodes);
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 = compile_internal(ir.value.body, ctx2, platform, asyncPlatformFns, platformDef, true, compilingNodes);
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
- try {
268
- await compiled_body(ctx2);
269
- }
270
- catch (e) {
271
- if (e instanceof ContinueException && e.label === label) {
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
- try {
302
- compiled_body(ctx2);
303
- }
304
- catch (e) {
305
- if (e instanceof ContinueException && e.label === label) {
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 = compile_internal(ir.value.set, ctx, platform, asyncPlatformFns, platformDef, false, compilingNodes);
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 = compile_internal(ir.value.body, ctx2, platform, asyncPlatformFns, platformDef, true, compilingNodes);
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
- try {
345
- await compiled_body(ctx2);
346
- }
347
- catch (e) {
348
- if (e instanceof ContinueException && e.label === label) {
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
- try {
378
- compiled_body(ctx2);
379
- }
380
- catch (e) {
381
- if (e instanceof ContinueException && e.label === label) {
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 = compile_internal(ir.value.dict, ctx, platform, asyncPlatformFns, platformDef, false, compilingNodes);
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 = compile_internal(ir.value.body, ctx2, platform, asyncPlatformFns, platformDef, true, compilingNodes);
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
- try {
425
- await compiled_body(ctx2);
426
- }
427
- catch (e) {
428
- if (e instanceof ContinueException && e.label === label) {
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
- try {
459
- compiled_body(ctx2);
460
- }
461
- catch (e) {
462
- if (e instanceof ContinueException && e.label === label) {
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 = compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, platformDef, false, compilingNodes);
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
- return async (ctx) => {
488
- throw new ReturnException(await compiled_value(ctx));
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
- return (ctx) => {
493
- throw new ReturnException(compiled_value(ctx));
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 label = ir.value.label;
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 label = ir.value.label;
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