@mark1russell7/coeval 0.13.0 → 0.15.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/build/index.d.ts +157 -104
- package/build/index.d.ts.map +1 -1
- package/build/index.js +533 -173
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agree.ts +110 -2
- package/src/evaluate.ts +114 -22
- package/src/formula.ts +17 -3
- package/src/index.ts +2 -2
- package/src/reference.ts +247 -42
package/src/reference.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type Effect,
|
|
11
11
|
type EvalFn,
|
|
12
12
|
type EvalInput,
|
|
13
|
+
type Journal,
|
|
13
14
|
type Machine,
|
|
14
15
|
type Part,
|
|
15
16
|
type Perform,
|
|
@@ -17,6 +18,8 @@ import {
|
|
|
17
18
|
type RunInput,
|
|
18
19
|
type RunPart,
|
|
19
20
|
type Steps,
|
|
21
|
+
type StopSignal,
|
|
22
|
+
JOURNAL,
|
|
20
23
|
makeContext,
|
|
21
24
|
permits,
|
|
22
25
|
placeText,
|
|
@@ -56,6 +59,17 @@ export interface ReferenceOptions {
|
|
|
56
59
|
readonly verb?: string;
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
/** The options of a run of the reference: the options of an evaluation, with the host and a signal (REF.RUN). */
|
|
63
|
+
export interface ReferenceRunOptions extends ReferenceOptions {
|
|
64
|
+
/**
|
|
65
|
+
* The host code that does each effect of a runner. A promise from it makes the run wait. Without it, an effect gives
|
|
66
|
+
* its payload. A journal on the function sets the order of the waits of each `all` (RUN.REPLAY).
|
|
67
|
+
*/
|
|
68
|
+
readonly perform?: Perform;
|
|
69
|
+
/** A signal of the host. When it aborts, the run stops with `#ABORT` (RUN.ABORT). */
|
|
70
|
+
readonly signal?: StopSignal;
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
/** The scope of the addresses of effects (RUN.ADDRESS): the root of the evaluation, or one item of `all`. */
|
|
60
74
|
interface Scope {
|
|
61
75
|
/** The text before each address of the scope. */
|
|
@@ -73,6 +87,10 @@ interface Env {
|
|
|
73
87
|
readonly effects: Effect[];
|
|
74
88
|
readonly maxDepth: number;
|
|
75
89
|
readonly perform: Perform | undefined;
|
|
90
|
+
/** The journal on the function `perform`, when it has one. It gets and gives the order of the waits of `all`. */
|
|
91
|
+
readonly journal: Journal | undefined;
|
|
92
|
+
/** `true` in a run: the host does each effect of a runner, and an effect of an evaluator gives `#CALC!`. */
|
|
93
|
+
readonly running: boolean;
|
|
76
94
|
/** The verb that goes up the ladder at each document. Each part evaluates with the same verb. */
|
|
77
95
|
readonly verb: string;
|
|
78
96
|
/** The scope of the code that evaluates at this time. `all` changes it for each item. */
|
|
@@ -209,6 +227,49 @@ function resolveFully(machine: Machine, ctx: Context, path: readonly unknown[],
|
|
|
209
227
|
* mock, the effect gives its payload (RUN.EVAL).
|
|
210
228
|
*/
|
|
211
229
|
function cause(env: Env, ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result {
|
|
230
|
+
const effect = recordEffect(env, ctx, kind, payload, place);
|
|
231
|
+
if ("ok" in effect) return effect;
|
|
232
|
+
effect.outcome = answer(env.perform, effect, place);
|
|
233
|
+
return effect.outcome;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* This function causes the effect of an evaluator. In a run, the host does each effect, thus only a runner can cause
|
|
238
|
+
* one. The effect then gives `#CALC!` before the test of its capability (RUN.NEEDS-RUNNER).
|
|
239
|
+
*/
|
|
240
|
+
function causeFromEvaluator(env: Env, ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result {
|
|
241
|
+
const text = typeof kind === "string" ? kind : kind.join(".");
|
|
242
|
+
if (env.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
|
|
243
|
+
return cause(env, ctx, kind, payload, place);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* This function causes the effect of a runner. In an evaluation, the mock answers it at once. In a run, the host does
|
|
248
|
+
* it, and a promise from the host makes the steps wait (RUN.SEQ). An effect whose wait a stop ends keeps no outcome
|
|
249
|
+
* (RUN.OUTCOME).
|
|
250
|
+
*/
|
|
251
|
+
function* causeAndWait(env: Env, ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Steps {
|
|
252
|
+
if (!env.running) return cause(env, ctx, kind, payload, place);
|
|
253
|
+
const effect = recordEffect(env, ctx, kind, payload, place);
|
|
254
|
+
if ("ok" in effect) return effect;
|
|
255
|
+
const perform = env.perform;
|
|
256
|
+
if (perform === undefined) return (effect.outcome = ok(payload));
|
|
257
|
+
let given: unknown;
|
|
258
|
+
try {
|
|
259
|
+
given = perform(effect);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return (effect.outcome = hostError(effect, place, error));
|
|
262
|
+
}
|
|
263
|
+
if (!(given instanceof Promise)) return (effect.outcome = ok(given));
|
|
264
|
+
const outcome = (yield given.then(
|
|
265
|
+
(value: unknown) => ok(value),
|
|
266
|
+
(error: unknown) => hostError(effect, place, error),
|
|
267
|
+
)) as Result;
|
|
268
|
+
return (effect.outcome = outcome);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** This function checks the capability of an effect, and records the effect with its address (RUN.CAP, RUN.ADDRESS). */
|
|
272
|
+
function recordEffect(env: Env, ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Failed | { -readonly [K in keyof Effect]: Effect[K] } {
|
|
212
273
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
213
274
|
const text = keys.join(".");
|
|
214
275
|
const machine = env.machine;
|
|
@@ -229,8 +290,13 @@ function cause(env: Env, ctx: Context, kind: Cap, payload: unknown, place: reado
|
|
|
229
290
|
address: `${env.scope.prefix}${at}#${count}`,
|
|
230
291
|
};
|
|
231
292
|
env.effects.push(effect);
|
|
232
|
-
|
|
233
|
-
|
|
293
|
+
return effect;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** This function gives the outcome of an effect that the host failed. A `Failure` gives its own error value (RUN.CAUSE). */
|
|
297
|
+
function hostError(effect: Effect, place: readonly Key[], error: unknown): Result {
|
|
298
|
+
if (error instanceof Failure) return placed({ ok: false, error: error.error }, place);
|
|
299
|
+
return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, { place, cause: error });
|
|
234
300
|
}
|
|
235
301
|
|
|
236
302
|
/**
|
|
@@ -243,8 +309,7 @@ function answer(perform: Perform | undefined, effect: Effect, place: readonly Ke
|
|
|
243
309
|
try {
|
|
244
310
|
given = perform(effect);
|
|
245
311
|
} catch (error) {
|
|
246
|
-
|
|
247
|
-
return fail("#CALC!", `The host failed the effect ${effect.kind}: ${describe(error)}`, { place, cause: error });
|
|
312
|
+
return hostError(effect, place, error);
|
|
248
313
|
}
|
|
249
314
|
if (!(given instanceof Promise)) return ok(given);
|
|
250
315
|
void given.catch(() => undefined);
|
|
@@ -321,7 +386,7 @@ function evalInput(env: Env, seen: Seen, at: At): EvalInput {
|
|
|
321
386
|
machine: env.machine,
|
|
322
387
|
...(at.address ? { address: true } : {}),
|
|
323
388
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
324
|
-
effect: (kind, payload) =>
|
|
389
|
+
effect: (kind, payload) => causeFromEvaluator(env, at.ctx, kind, payload, at.place),
|
|
325
390
|
fill,
|
|
326
391
|
};
|
|
327
392
|
}
|
|
@@ -349,67 +414,124 @@ function runInput(env: Env, seen: Seen, at: At): RunInput {
|
|
|
349
414
|
place: at.place,
|
|
350
415
|
machine: env.machine,
|
|
351
416
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
352
|
-
effect:
|
|
353
|
-
return cause(env, at.ctx, kind, payload, at.place);
|
|
354
|
-
},
|
|
417
|
+
effect: (kind, payload) => causeAndWait(env, at.ctx, kind, payload, at.place),
|
|
355
418
|
all: (list, options) => together(env, list, options),
|
|
356
419
|
fill,
|
|
357
420
|
};
|
|
358
421
|
}
|
|
359
422
|
|
|
423
|
+
/** A part of `all` that waits, and the promise that gives its next input. */
|
|
424
|
+
interface Waiter {
|
|
425
|
+
readonly index: number;
|
|
426
|
+
readonly steps: Steps;
|
|
427
|
+
next: Promise<Resumed>;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** The input of a part after its wait: the value of the promise, or its rejection. */
|
|
431
|
+
interface Resumed {
|
|
432
|
+
readonly waiter: Waiter;
|
|
433
|
+
readonly value?: unknown;
|
|
434
|
+
readonly error?: unknown;
|
|
435
|
+
readonly threw: boolean;
|
|
436
|
+
}
|
|
437
|
+
|
|
360
438
|
/**
|
|
361
439
|
* This function evaluates parts for `all` (RUN.ALL). The parts start in their order, up to the limit, and each part
|
|
362
|
-
* goes on until it ends or waits.
|
|
363
|
-
*
|
|
364
|
-
*
|
|
440
|
+
* goes on until it ends or waits. A part that waits keeps its place in the limit. With `failFast`, the first failure
|
|
441
|
+
* stops the parts that wait, and the later parts do not start (RUN.FAIL-FAST).
|
|
442
|
+
*
|
|
443
|
+
* When a part waits, the steps wait for the next part that settles. The journal of the host can choose that part
|
|
444
|
+
* (RUN.REPLAY), and it gets each part that settled (RUN.RECORD). In an evaluation, the drive stops at the first wait
|
|
445
|
+
* and gives `#CALC!`. In a run, the host settles the wait, and the part goes on in its own scope.
|
|
365
446
|
*
|
|
366
447
|
* Each part has its own scope of addresses. Its prefix names the use of `all` in the scope around it, and the part
|
|
367
448
|
* (RUN.ADDRESS).
|
|
368
449
|
*/
|
|
369
450
|
function* together(env: Env, list: readonly RunPart[], options: AllOptions = {}): Generator<Promise<unknown>, readonly Result[], unknown> {
|
|
370
451
|
const limit = Math.max(1, options.concurrency ?? list.length);
|
|
452
|
+
const failFast = options.failFast === true;
|
|
371
453
|
const results: (Result | undefined)[] = list.map(() => undefined);
|
|
372
|
-
const
|
|
454
|
+
const scopes: Scope[] = [];
|
|
455
|
+
const waiting: Waiter[] = [];
|
|
373
456
|
const outer = env.scope;
|
|
374
457
|
const address = `${outer.prefix}all${outer.alls}`;
|
|
375
458
|
outer.alls += 1;
|
|
376
459
|
let failed = false;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
460
|
+
let started = 0;
|
|
461
|
+
const end = (index: number, result: Result): void => {
|
|
462
|
+
results[index] = result;
|
|
463
|
+
if (failFast && !result.ok) failed = true;
|
|
464
|
+
};
|
|
465
|
+
/** This function gives a part its input in its own scope, and keeps it in the list when it waits again. */
|
|
466
|
+
const goOn = (index: number, steps: Steps, input: Omit<Resumed, "waiter">): void => {
|
|
467
|
+
const around = env.scope;
|
|
468
|
+
env.scope = scopes[index] as Scope;
|
|
469
|
+
let step: IteratorResult<Promise<unknown>, Result>;
|
|
380
470
|
try {
|
|
381
|
-
|
|
382
|
-
const step = steps.next();
|
|
383
|
-
if (step.done === true) result = step.value;
|
|
384
|
-
else waiting.push({ steps, promise: step.value });
|
|
471
|
+
step = input.threw ? steps.throw(input.error) : steps.next(input.value);
|
|
385
472
|
} catch (error) {
|
|
386
|
-
|
|
473
|
+
end(index, thrown(error, "a part"));
|
|
474
|
+
return;
|
|
387
475
|
} finally {
|
|
388
|
-
env.scope =
|
|
476
|
+
env.scope = around;
|
|
389
477
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
478
|
+
if (step.done === true) {
|
|
479
|
+
end(index, step.value);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const waiter: Waiter = { index, steps, next: Promise.resolve() as unknown as Promise<Resumed> };
|
|
483
|
+
waiter.next = step.value.then(
|
|
484
|
+
(value: unknown): Resumed => ({ waiter, value, threw: false }),
|
|
485
|
+
(error: unknown): Resumed => ({ waiter, error, threw: true }),
|
|
486
|
+
);
|
|
487
|
+
waiting.push(waiter);
|
|
488
|
+
};
|
|
489
|
+
/** This function starts the next parts in their order, while the limit permits it and no part failed. */
|
|
490
|
+
const startMore = (): void => {
|
|
491
|
+
while (!failed && waiting.length < limit && started < list.length) {
|
|
492
|
+
const index = started;
|
|
493
|
+
started += 1;
|
|
494
|
+
scopes[index] = { prefix: `${address}.${index}/`, counts: new Map(), alls: 0 };
|
|
495
|
+
const around = env.scope;
|
|
496
|
+
env.scope = scopes[index];
|
|
497
|
+
let steps: Steps;
|
|
396
498
|
try {
|
|
397
|
-
|
|
398
|
-
} catch {
|
|
399
|
-
|
|
499
|
+
steps = (list[index] as RunPart).evaluate();
|
|
500
|
+
} catch (error) {
|
|
501
|
+
end(index, thrown(error, "a part"));
|
|
502
|
+
continue;
|
|
503
|
+
} finally {
|
|
504
|
+
env.scope = around;
|
|
400
505
|
}
|
|
506
|
+
goOn(index, steps, { threw: false });
|
|
401
507
|
}
|
|
402
|
-
|
|
508
|
+
};
|
|
509
|
+
startMore();
|
|
510
|
+
while (waiting.length > 0 && !failed) {
|
|
511
|
+
const chosen = env.journal?.next?.(address, waiting.map((waiter) => waiter.index));
|
|
512
|
+
const pick = chosen === undefined ? undefined : waiting.find((waiter) => waiter.index === chosen);
|
|
513
|
+
const resumed = (yield pick === undefined ? Promise.race(waiting.map((waiter) => waiter.next)) : pick.next) as Resumed;
|
|
514
|
+
env.journal?.settled?.(address, resumed.waiter.index);
|
|
515
|
+
waiting.splice(waiting.indexOf(resumed.waiter), 1);
|
|
516
|
+
goOn(resumed.waiter.index, resumed.waiter.steps, resumed);
|
|
517
|
+
startMore();
|
|
403
518
|
}
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
519
|
+
if (!failed) return results as readonly Result[];
|
|
520
|
+
const stopped = fail("#ABORT", "The part stopped: a part before it failed, and the parts run with failFast.");
|
|
521
|
+
for (const waiter of waiting) {
|
|
522
|
+
try {
|
|
523
|
+
waiter.steps.return(stopped);
|
|
524
|
+
} catch {
|
|
525
|
+
// The finally code of a part threw. The part stops with the abort.
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return results.map((result) => result ?? stopped);
|
|
407
529
|
}
|
|
408
530
|
|
|
409
531
|
/**
|
|
410
532
|
* This function evaluates a document by the rules, without a compilation. It is the reference of the plans: a plan,
|
|
411
|
-
* a traced plan and the second tier of a language must give its results.
|
|
412
|
-
*
|
|
533
|
+
* a traced plan and the second tier of a language must give its results. In an evaluation, an effect gives the answer
|
|
534
|
+
* of the mock or its payload, and a part that waits gives `#CALC!`. `referenceRun` reads the rules of a run.
|
|
413
535
|
*
|
|
414
536
|
* The verb of the options goes up the ladder at each document, as in `prepare`. Each verb has the same rules: the
|
|
415
537
|
* ladder, the contract of the kind and the sealed parts (VERB.AGREE).
|
|
@@ -421,27 +543,110 @@ function* together(env: Env, list: readonly RunPart[], options: AllOptions = {})
|
|
|
421
543
|
* @returns The result.
|
|
422
544
|
*/
|
|
423
545
|
export function reference(machine: Machine, doc: unknown, context: Context = makeContext(), options: ReferenceOptions = {}): Result {
|
|
424
|
-
const env
|
|
546
|
+
const env = envOf(machine, options, false);
|
|
547
|
+
try {
|
|
548
|
+
return within(env, () => evaluateAt(env, rootAt(machine, doc, context, options)));
|
|
549
|
+
} catch (error) {
|
|
550
|
+
return placed(thrown(error, "the reference"), []);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* This function starts a run of a document by the rules, without a compilation (REF.RUN). The host does each effect of a runner,
|
|
556
|
+
* and a step waits when the host waits (RUN.SEQ). `all` settles the waits in the order of the host, or in the order
|
|
557
|
+
* of the journal of a replay (RUN.REPLAY). An effect of an evaluator gives `#CALC!` (RUN.NEEDS-RUNNER). A signal that
|
|
558
|
+
* aborts stops the run with `#ABORT` (RUN.ABORT).
|
|
559
|
+
*
|
|
560
|
+
* The part of the run before the first wait happens at once, in the call (RUN.EAGER). `agreeRun` compares the run of
|
|
561
|
+
* a plan with this run.
|
|
562
|
+
*
|
|
563
|
+
* @param machine - The world, the evaluators and the runners.
|
|
564
|
+
* @param doc - The document.
|
|
565
|
+
* @param context - The names and the capabilities at the root.
|
|
566
|
+
* @param options - The options of `reference`, the host and a signal.
|
|
567
|
+
* @returns The result, when the run ends.
|
|
568
|
+
*/
|
|
569
|
+
export function referenceRun(machine: Machine, doc: unknown, context: Context = makeContext(), options: ReferenceRunOptions = {}): Promise<Result> {
|
|
570
|
+
const env = envOf(machine, options, true);
|
|
571
|
+
const steps = stepsAt(env, rootAt(machine, doc, context, options));
|
|
572
|
+
return driveRun(env, steps, options.signal).catch((error: unknown) => placed(thrown(error, "the reference"), []));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/** This function makes the state of the reference for one evaluation or one run. */
|
|
576
|
+
function envOf(machine: Machine, options: ReferenceOptions, running: boolean): Env {
|
|
577
|
+
const perform = options.perform;
|
|
578
|
+
return {
|
|
425
579
|
machine,
|
|
426
580
|
definitions: options.definitions ?? {},
|
|
427
581
|
effects: options.effects ?? [],
|
|
428
582
|
maxDepth: machine.maxDepth ?? 1000,
|
|
429
|
-
perform
|
|
583
|
+
perform,
|
|
584
|
+
journal: perform === undefined ? undefined : (perform as { readonly [JOURNAL]?: Journal })[JOURNAL],
|
|
585
|
+
running,
|
|
430
586
|
verb: options.verb ?? "eval",
|
|
431
587
|
scope: { prefix: "", counts: new Map(), alls: 0 },
|
|
432
588
|
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** This function gives the root position of a document. A context without a world gets the world of the machine. */
|
|
592
|
+
function rootAt(machine: Machine, doc: unknown, context: Context, options: ReferenceOptions): At {
|
|
433
593
|
const ctx = context.world === null ? { ...context, world: worldThing(machine.world) } : context;
|
|
434
|
-
|
|
594
|
+
return { value: doc, place: [], declared: options.declaredType, depth: 0, address: false, ctx };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* This function does one synchronous step of an evaluation or a run. A node of a tree reads the definitions of the
|
|
599
|
+
* evaluation for its equations (TREE.LADDER). Thus the state of the evaluation is the innermost one in the step.
|
|
600
|
+
*/
|
|
601
|
+
function within<T>(env: Env, step: () => T): T {
|
|
435
602
|
activeEnvs.push(env);
|
|
436
603
|
try {
|
|
437
|
-
return
|
|
438
|
-
} catch (error) {
|
|
439
|
-
return placed(thrown(error, "the reference"), []);
|
|
604
|
+
return step();
|
|
440
605
|
} finally {
|
|
441
606
|
activeEnvs.pop();
|
|
442
607
|
}
|
|
443
608
|
}
|
|
444
609
|
|
|
610
|
+
/** The mark of a signal that aborted. */
|
|
611
|
+
const STOPPED: unique symbol = /* @__PURE__ */ Symbol("stopped");
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* This function drives the steps of a run. Each step waits for its promise. A rejection goes into the steps as an
|
|
615
|
+
* exception. A signal that aborts stops the steps with `#ABORT`, also before the first step (RUN.ABORT).
|
|
616
|
+
*/
|
|
617
|
+
async function driveRun(env: Env, steps: Steps, signal: StopSignal | undefined): Promise<Result> {
|
|
618
|
+
const stop = (): Result => {
|
|
619
|
+
const why = signal?.reason ?? "no reason";
|
|
620
|
+
const aborted = fail("#ABORT", `The host stopped the run: ${describe(why)}`, { cause: signal?.reason });
|
|
621
|
+
try {
|
|
622
|
+
within(env, () => steps.return(aborted));
|
|
623
|
+
} catch {
|
|
624
|
+
// The finally code of a runner threw. The run stops with the abort.
|
|
625
|
+
}
|
|
626
|
+
return aborted;
|
|
627
|
+
};
|
|
628
|
+
if (signal?.aborted === true) return stop();
|
|
629
|
+
const stopping =
|
|
630
|
+
signal === undefined
|
|
631
|
+
? undefined
|
|
632
|
+
: new Promise<typeof STOPPED>((resolve) => {
|
|
633
|
+
signal.addEventListener("abort", () => resolve(STOPPED), { once: true });
|
|
634
|
+
});
|
|
635
|
+
let step = within(env, () => steps.next());
|
|
636
|
+
while (step.done !== true) {
|
|
637
|
+
let input: unknown;
|
|
638
|
+
try {
|
|
639
|
+
input = await (stopping === undefined ? step.value : Promise.race([step.value, stopping]));
|
|
640
|
+
} catch (error) {
|
|
641
|
+
step = within(env, () => steps.throw(error));
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (input === STOPPED) return stop();
|
|
645
|
+
step = within(env, () => steps.next(input));
|
|
646
|
+
}
|
|
647
|
+
return step.value;
|
|
648
|
+
}
|
|
649
|
+
|
|
445
650
|
/** The evaluations of the reference that run at this time, the innermost last. */
|
|
446
651
|
const activeEnvs: Env[] = [];
|
|
447
652
|
|