@mark1russell7/coeval 0.27.0 → 0.27.1
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 +19 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1416 -1341
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/evaluate.ts +7 -2
- package/src/formula.ts +6 -1
- package/src/generate.ts +6 -1
- package/src/index.ts +1 -1
- package/src/language.ts +51 -1
- package/src/reference.ts +12 -4
- package/src/stepper.ts +49 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@mark1russell7/coeval",
|
|
4
|
-
"version": "0.27.
|
|
4
|
+
"version": "0.27.1",
|
|
5
5
|
"description": "coeval: the kernel of declarative languages. Documents in envelopes, a ladder that finds the evaluator, kind contracts, compiled plans, a static kit, a formula kit and a run side with capabilities.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./build/index.js",
|
package/src/evaluate.ts
CHANGED
|
@@ -1199,6 +1199,11 @@ class Session implements Runtime {
|
|
|
1199
1199
|
declare work?: number;
|
|
1200
1200
|
/** The count of the applications of closures that run at this time. `formula.fn` writes it (FORMULA.CALL-DEPTH). */
|
|
1201
1201
|
declare applying?: number;
|
|
1202
|
+
/**
|
|
1203
|
+
* The count of the applications of closures that run at this time, on the root and on each runtime of an equation
|
|
1204
|
+
* (WORK.AFTER). `formula.fn` writes it on the runtime of the root. The first application adds the field.
|
|
1205
|
+
*/
|
|
1206
|
+
declare active?: number;
|
|
1202
1207
|
readonly machine: Machine;
|
|
1203
1208
|
/**
|
|
1204
1209
|
* The epoch of the evaluation: the list of capabilities that the host gave the root (CAP.NARROW-KEPT). A context
|
|
@@ -1268,14 +1273,14 @@ class Session implements Runtime {
|
|
|
1268
1273
|
/**
|
|
1269
1274
|
* This function adds work to the count of the evaluation (WORK.LIMIT). One count is on the runtime of the root, thus
|
|
1270
1275
|
* each equation of a tree adds to it. After the end of the evaluation, each entry from the host starts a new count
|
|
1271
|
-
* (WORK.AFTER).
|
|
1276
|
+
* (WORK.AFTER). An entry is a point with no application of a closure that runs, on any runtime of the evaluation.
|
|
1272
1277
|
*
|
|
1273
1278
|
* @param points - The work.
|
|
1274
1279
|
* @throws {Failure} The error `#WORK!`, when the count is over the limit of the machine.
|
|
1275
1280
|
*/
|
|
1276
1281
|
spend(points: number): void {
|
|
1277
1282
|
const root = this.top ?? this;
|
|
1278
|
-
const entry = root.top === root && (
|
|
1283
|
+
const entry = root.top === root && (root.active ?? 0) === 0;
|
|
1279
1284
|
const work = (entry ? 0 : (root.work ?? 0)) + points;
|
|
1280
1285
|
root.work = work;
|
|
1281
1286
|
const limit = this.machine.maxWork ?? WORK_LIMIT;
|
package/src/formula.ts
CHANGED
|
@@ -139,7 +139,7 @@ export function isClosure(value: unknown): value is Closure {
|
|
|
139
139
|
* The runtime of an evaluation with the clock of `call` (FORMULA.CALL-DEPTH, D125): the count of the applications of
|
|
140
140
|
* closures that run at this time. The first application adds the field, thus an evaluation without one has no field.
|
|
141
141
|
*/
|
|
142
|
-
type Clocked = Runtime & { applying?: number };
|
|
142
|
+
type Clocked = Runtime & { applying?: number; active?: number; top?: Clocked | undefined };
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
145
|
* The standard extensions: the values that JSON cannot write. `ext.domain` reads a member of a domain through the
|
|
@@ -1047,7 +1047,11 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
|
|
|
1047
1047
|
if (level > limit) throw new Failure(fail("#DEPTH!", `The applications of functions nest deeper than ${limit} levels.`).error);
|
|
1048
1048
|
// WORK.LIMIT: an application is a place where work can multiply, thus each one counts one point (D134).
|
|
1049
1049
|
rt.spend?.(1);
|
|
1050
|
+
// WORK.AFTER: the runtime of the root counts the applications of each runtime of the evaluation. An equation of
|
|
1051
|
+
// a tree in the body has its own runtime, and its work does not start a new count.
|
|
1052
|
+
const root = clock.top ?? clock;
|
|
1050
1053
|
clock.applying = level;
|
|
1054
|
+
root.active = (root.active ?? 0) + 1;
|
|
1051
1055
|
try {
|
|
1052
1056
|
const scope: Record<string, unknown> = { ...ctx.names };
|
|
1053
1057
|
names.forEach((name, index) => setOwn(scope, name, args[index]));
|
|
@@ -1056,6 +1060,7 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
|
|
|
1056
1060
|
return result.value;
|
|
1057
1061
|
} finally {
|
|
1058
1062
|
clock.applying = level - 1;
|
|
1063
|
+
root.active = (root.active ?? 1) - 1;
|
|
1059
1064
|
}
|
|
1060
1065
|
};
|
|
1061
1066
|
Object.defineProperty(closure, CLOSURE, { value: { params: names } });
|
package/src/generate.ts
CHANGED
|
@@ -198,7 +198,12 @@ export function generate(world: World, options: GenerateOptions): unknown {
|
|
|
198
198
|
const envelope = (type: readonly Key[], value: Json, header?: string): Json => {
|
|
199
199
|
const out: Record<string, Json> = { type: renderType(type), value };
|
|
200
200
|
if (header !== undefined) out["name"] = header;
|
|
201
|
-
if (options.own !== undefined && options.own.length > 0 && rng.chance(0.03))
|
|
201
|
+
if (options.own !== undefined && options.own.length > 0 && rng.chance(0.03)) {
|
|
202
|
+
const code = rng.pick(options.own);
|
|
203
|
+
// The code of a kind binds the names of its scope rules (C7). An own entry of other code does not, thus a kind
|
|
204
|
+
// with scope rules gets no own entry (GEN.CONTRACTS). The draws stay, thus each other document stays the same.
|
|
205
|
+
if ((kindOf(type)?.scope ?? []).length === 0) out["own"] = { eval: code };
|
|
206
|
+
}
|
|
202
207
|
return out;
|
|
203
208
|
};
|
|
204
209
|
|
package/src/index.ts
CHANGED
|
@@ -91,7 +91,7 @@ export {
|
|
|
91
91
|
restrictMachine,
|
|
92
92
|
restrictWorld,
|
|
93
93
|
} from "./merge.ts";
|
|
94
|
-
export { type Language, type LanguageCode, type LanguageDefinition, compose, defineLanguage } from "./language.ts";
|
|
94
|
+
export { type Language, type LanguageCode, type LanguageDefinition, boundary, compose, defineLanguage } from "./language.ts";
|
|
95
95
|
export {
|
|
96
96
|
type Answer,
|
|
97
97
|
type AskOptions,
|
package/src/language.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Key, isRecord } from "./doc.ts";
|
|
2
|
-
import type
|
|
2
|
+
import { type InputWithRuntime, type Machine, type Plan, RUNTIME, evaluateWithin, makeContext, planner } from "./evaluate.ts";
|
|
3
|
+
import { fail } from "./errors.ts";
|
|
3
4
|
import { type MachinePart, type MergeConflict, type WorldPart, MergeError, mergeMachines } from "./merge.ts";
|
|
4
5
|
|
|
5
6
|
/** The code half of a language: each field of a machine part, without the world. */
|
|
@@ -145,3 +146,52 @@ export function compose(...languages: readonly Language[]): Machine {
|
|
|
145
146
|
if (conflicts.length > 0) throw new MergeError(conflicts);
|
|
146
147
|
return mergeMachines(...parts);
|
|
147
148
|
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* This function makes the language of the boundary kind `lang` (K38, D193). A document of the kind holds the name of a
|
|
152
|
+
* language and a document in that language: `{ type: "lang", value: { language, doc } }`. The machine of the named
|
|
153
|
+
* language prepares and evaluates the inner document, as a module of another language (COMPOSE.BOUNDARY).
|
|
154
|
+
*
|
|
155
|
+
* The boundary is closed. The inner document gets no names, no capabilities and no node as `self`. Only its result
|
|
156
|
+
* crosses the boundary. Thus an effect in the inner document fails with `#CAP!`, and the inner machine cannot read the
|
|
157
|
+
* names of the outer document.
|
|
158
|
+
*
|
|
159
|
+
* @param machines - The machine of each language, by the name that a document gives.
|
|
160
|
+
* @returns The language, for `compose`.
|
|
161
|
+
*/
|
|
162
|
+
export function boundary(machines: Readonly<Record<string, Machine>>): Language {
|
|
163
|
+
const plans = new Map<string, (doc: unknown) => Plan>();
|
|
164
|
+
const planOf = (name: string): ((doc: unknown) => Plan) | undefined => {
|
|
165
|
+
let found = plans.get(name);
|
|
166
|
+
if (found === undefined && Object.hasOwn(machines, name)) {
|
|
167
|
+
found = planner(machines[name] as Machine);
|
|
168
|
+
plans.set(name, found);
|
|
169
|
+
}
|
|
170
|
+
return found;
|
|
171
|
+
};
|
|
172
|
+
return defineLanguage({
|
|
173
|
+
name: "boundary",
|
|
174
|
+
version: "1.0.0",
|
|
175
|
+
world: {
|
|
176
|
+
kinds: {
|
|
177
|
+
lang: {
|
|
178
|
+
verbs: { eval: "boundary.lang" },
|
|
179
|
+
summary: "A document in another language. The machine of that language evaluates it, with no names and no capabilities.",
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
code: {
|
|
184
|
+
evaluators: {
|
|
185
|
+
"boundary.lang": (input) => {
|
|
186
|
+
const { data, place } = input;
|
|
187
|
+
if (!isRecord(data) || typeof data["language"] !== "string" || !("doc" in data)) {
|
|
188
|
+
return fail("#VALUE!", "A document of the kind lang has the value { language, doc }.", { place });
|
|
189
|
+
}
|
|
190
|
+
const plans = planOf(data["language"]);
|
|
191
|
+
if (plans === undefined) return fail("#NAME?", `The boundary knows no language ${JSON.stringify(data["language"])}.`, { place });
|
|
192
|
+
return evaluateWithin(plans(data["doc"]), makeContext(), (input as InputWithRuntime)[RUNTIME]);
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
}
|
package/src/reference.ts
CHANGED
|
@@ -119,6 +119,8 @@ interface Meter {
|
|
|
119
119
|
spent: number;
|
|
120
120
|
/** `true` after the end of the evaluation. Then each entry from the host starts a new count (WORK.AFTER). */
|
|
121
121
|
ended: boolean;
|
|
122
|
+
/** The count of the applications of closures that run at this time, in each state of the evaluation (WORK.AFTER). */
|
|
123
|
+
active: number;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
/** The state of one evaluation of the reference. */
|
|
@@ -731,7 +733,7 @@ function evaluateRoot(machine: Machine, doc: unknown, context: Context, options:
|
|
|
731
733
|
*/
|
|
732
734
|
function spend(env: Pick<Env, "maxWork" | "meter" | "applying">, points: number): void {
|
|
733
735
|
const meter = env.meter;
|
|
734
|
-
if (meter.ended &&
|
|
736
|
+
if (meter.ended && meter.active === 0) meter.spent = 0;
|
|
735
737
|
meter.spent += points;
|
|
736
738
|
if (meter.spent > env.maxWork) throw new Failure(workFailure(env.maxWork));
|
|
737
739
|
}
|
|
@@ -781,7 +783,7 @@ function envOf(machine: Machine, options: ReferenceOptions, running: boolean, ro
|
|
|
781
783
|
held: 0,
|
|
782
784
|
applying: 0,
|
|
783
785
|
maxWork: machine.maxWork ?? WORK_LIMIT,
|
|
784
|
-
meter: meter ?? { spent: 0, ended: false },
|
|
786
|
+
meter: meter ?? { spent: 0, ended: false, active: 0 },
|
|
785
787
|
nested: meter !== undefined,
|
|
786
788
|
reading: outer ?? (options.reads === undefined ? undefined : { list: options.reads, root }),
|
|
787
789
|
};
|
|
@@ -1521,22 +1523,28 @@ export function formulaDefinitions(profile: FormulaProfile = {}): Definitions {
|
|
|
1521
1523
|
const names = params as readonly string[];
|
|
1522
1524
|
const body = partNamed(parts, "body") as Part;
|
|
1523
1525
|
// FORMULA.CALL-DEPTH: the evaluation that makes the closure counts its applications. A quarter of maxDepth is the limit.
|
|
1524
|
-
const
|
|
1526
|
+
const maker = activeEnvs[activeEnvs.length - 1];
|
|
1527
|
+
const clock = maker ?? { applying: 0, maxDepth: 1000, maxWork: WORK_LIMIT, meter: { spent: 0, ended: false, active: 0 } };
|
|
1525
1528
|
const limit = Math.ceil(clock.maxDepth / 4);
|
|
1526
1529
|
const closure = (...args: readonly unknown[]): unknown => {
|
|
1527
1530
|
if (clock.applying >= limit) throw new Failure(fail("#DEPTH!", `The applications of functions nest deeper than ${limit} levels.`).error);
|
|
1528
1531
|
// WORK.LIMIT: each application counts one point, after the clock of its depth.
|
|
1529
1532
|
spend(clock, 1);
|
|
1530
1533
|
clock.applying += 1;
|
|
1534
|
+
clock.meter.active += 1;
|
|
1531
1535
|
try {
|
|
1532
1536
|
const scope: Record<string, unknown> = {};
|
|
1533
1537
|
for (const key of Object.keys(context.names)) setField(scope, key, context.names[key]);
|
|
1534
1538
|
names.forEach((name, index) => setField(scope, name, args[index]));
|
|
1535
|
-
|
|
1539
|
+
// The body evaluates in the state of the evaluation that made the closure, also for a host after the end. Thus a
|
|
1540
|
+
// node of a tree in the body has the machine for its equations (WORK.AFTER).
|
|
1541
|
+
const scoped: Context = { ...context, names: scope };
|
|
1542
|
+
const result = maker === undefined ? body.evaluate(scoped) : within(maker, () => body.evaluate(scoped));
|
|
1536
1543
|
if (!result.ok) throw new Failure(result.error);
|
|
1537
1544
|
return result.value;
|
|
1538
1545
|
} finally {
|
|
1539
1546
|
clock.applying -= 1;
|
|
1547
|
+
clock.meter.active -= 1;
|
|
1540
1548
|
}
|
|
1541
1549
|
};
|
|
1542
1550
|
Object.defineProperty(closure, CLOSURE, { value: { params: names } });
|
package/src/stepper.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type Envelope, type Json, type Key, type TypeRef, docEquals, field, isEnvelope, isRecord, typeName } from "./doc.ts";
|
|
2
2
|
import { type Cap, type Context, type Resolution, type ResolveOptions, type Thing, isHostThing, worldThing } from "./deref.ts";
|
|
3
3
|
import { type EvalError, type Result, Failure, describe, fail, ok, placed, thrown } from "./errors.ts";
|
|
4
|
-
import { type Effect, type EvalInput, type Machine, type Part, type Perform, intersectCaps, makeContext, permits, placeText } from "./evaluate.ts";
|
|
4
|
+
import { type Effect, type EvalInput, type Machine, type Part, type Perform, WORK_LIMIT, intersectCaps, makeContext, permits, placeText, workFailure } from "./evaluate.ts";
|
|
5
5
|
import { type FormulaProfile, type OpFn, Held, STD_EXT } from "./formula.ts";
|
|
6
6
|
import type { World } from "./ladder.ts";
|
|
7
7
|
import { pathMatches } from "./parts.ts";
|
|
8
|
-
import type
|
|
8
|
+
import { type Definitions, STD_DEFINITIONS } from "./reference.ts";
|
|
9
|
+
import { STD_EVALUATORS } from "./stdlib.ts";
|
|
9
10
|
import { type CapsRule, type Kind, type Shape, type WorldEntry, isAlias, isKind, isNamespace } from "./world.ts";
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -111,6 +112,12 @@ interface Shared {
|
|
|
111
112
|
readonly applying: number;
|
|
112
113
|
/** `true` after a closure or a held error goes in a value. Only then does a value need a change for the host. */
|
|
113
114
|
readonly marked: boolean;
|
|
115
|
+
/** The work of the evaluation after the last call of `stepperNext` (WORK.LIMIT). A configuration without it has done no work. */
|
|
116
|
+
readonly work?: number;
|
|
117
|
+
/** The number of earlier effects at each place, after the last call of `stepperNext` (RUN.ADDRESS). */
|
|
118
|
+
readonly counts?: Readonly<Record<string, number>>;
|
|
119
|
+
/** The number of effects of the evaluation, after the last call of `stepperNext`. */
|
|
120
|
+
readonly effected?: number;
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
/**
|
|
@@ -164,8 +171,16 @@ interface Program {
|
|
|
164
171
|
readonly perform: Perform | undefined;
|
|
165
172
|
/** The number of earlier effects at each place, by the text of the place (RUN.ADDRESS). */
|
|
166
173
|
readonly counts: Map<string, number>;
|
|
174
|
+
/** The number of effects of the evaluation before the list `effects`. */
|
|
175
|
+
before: number;
|
|
167
176
|
/** The count of the applications at the last call of host code. */
|
|
168
177
|
applying: number;
|
|
178
|
+
/** The most work of one evaluation (WORK.LIMIT). */
|
|
179
|
+
readonly maxWork: number;
|
|
180
|
+
/** The work of the evaluation: one point for each application of a closure (WORK.LIMIT). */
|
|
181
|
+
work: number;
|
|
182
|
+
/** `true` after the end of the evaluation. Then each application from the host starts a new count (WORK.AFTER). */
|
|
183
|
+
ended: boolean;
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
const CLOSURE_MARK = "coeval.closure";
|
|
@@ -555,6 +570,9 @@ function apply(p: Program, c: Configuration, target: unknown, args: readonly unk
|
|
|
555
570
|
}
|
|
556
571
|
const limit = Math.ceil(p.maxDepth / 4);
|
|
557
572
|
if (c.applying >= limit) return back(c, fail("#DEPTH!", `The applications of functions nest deeper than ${limit} levels.`));
|
|
573
|
+
// WORK.LIMIT: each application counts one point, after the clock of its depth.
|
|
574
|
+
p.work += 1;
|
|
575
|
+
if (p.work > p.maxWork) return back(c, { ok: false, error: workFailure(p.maxWork) });
|
|
558
576
|
const frame: Frame = { f: "return", place: closure.place.concat(closure.body.path) };
|
|
559
577
|
pushed(c, frame);
|
|
560
578
|
const start = enter(c, { at: NO_KEYS }, closure.site, closure.body, scopeOf(closure, args));
|
|
@@ -576,6 +594,11 @@ function hostClosure(p: Program, value: object, closure: Closure): unknown {
|
|
|
576
594
|
const fn = (...args: readonly unknown[]): unknown => {
|
|
577
595
|
const limit = Math.ceil(p.maxDepth / 4);
|
|
578
596
|
if (p.applying >= limit) throw new Failure(fail("#DEPTH!", `The applications of functions nest deeper than ${limit} levels.`).error);
|
|
597
|
+
// WORK.AFTER: after the end of the evaluation, each application from the host starts a new count.
|
|
598
|
+
p.work = (p.ended && p.applying === 0 ? 0 : p.work) + 1;
|
|
599
|
+
if (p.work > p.maxWork) throw new Failure(workFailure(p.maxWork));
|
|
600
|
+
// Host code in the nested stepper writes the count of the applications, thus the call puts back the count of its start.
|
|
601
|
+
const before = p.applying;
|
|
579
602
|
p.applying += 1;
|
|
580
603
|
try {
|
|
581
604
|
const place = closure.place.concat(closure.body.path);
|
|
@@ -584,7 +607,7 @@ function hostClosure(p: Program, value: object, closure: Closure): unknown {
|
|
|
584
607
|
if (!end.result.ok) throw new Failure(end.result.error);
|
|
585
608
|
return forHost(p, end, end.result.value);
|
|
586
609
|
} finally {
|
|
587
|
-
p.applying
|
|
610
|
+
p.applying = before;
|
|
588
611
|
}
|
|
589
612
|
};
|
|
590
613
|
Object.defineProperty(fn, CLOSURE, { value: { params: closure.params } });
|
|
@@ -856,7 +879,7 @@ function cause(p: Program, ctx: Context, kind: Cap, payload: unknown, place: rea
|
|
|
856
879
|
const count = counts.get(at) ?? 0;
|
|
857
880
|
counts.set(at, count + 1);
|
|
858
881
|
const effect: { -readonly [K in keyof Effect]: Effect[K] } = {
|
|
859
|
-
index: effects.length,
|
|
882
|
+
index: p.before + effects.length,
|
|
860
883
|
kind: text,
|
|
861
884
|
path: keys,
|
|
862
885
|
payload,
|
|
@@ -948,7 +971,9 @@ function evaluateDoc(p: Program, c: Evaluating): Configuration {
|
|
|
948
971
|
return back(c, thrown(error, `the evaluator ${name}`));
|
|
949
972
|
}
|
|
950
973
|
}
|
|
951
|
-
|
|
974
|
+
// A standard evaluator gets the definition of the reference, unless the options give another definition (REF.TREE).
|
|
975
|
+
const standard = code === own(STD_EVALUATORS, name) ? own(STD_DEFINITIONS.evaluators, name) : undefined;
|
|
976
|
+
const defined = own(p.definitions.evaluators, name) ?? standard;
|
|
952
977
|
if (defined !== undefined) return hostEvaluator(p, c, name, defined, doc, keys, read.data, read.slots, site);
|
|
953
978
|
return back(c, fail("#NOEVAL", `The stepper has no definition of the compiled evaluator ${name}.`));
|
|
954
979
|
}
|
|
@@ -1087,7 +1112,11 @@ function programOf(machine: Machine, options: StepperOptions): Program {
|
|
|
1087
1112
|
effects: options.effects ?? [],
|
|
1088
1113
|
perform: options.perform,
|
|
1089
1114
|
counts: new Map(),
|
|
1115
|
+
before: 0,
|
|
1090
1116
|
applying: 0,
|
|
1117
|
+
maxWork: machine.maxWork ?? WORK_LIMIT,
|
|
1118
|
+
work: 0,
|
|
1119
|
+
ended: false,
|
|
1091
1120
|
};
|
|
1092
1121
|
}
|
|
1093
1122
|
|
|
@@ -1124,7 +1153,20 @@ export function stepperNext(machine: Machine, configuration: Configuration, opti
|
|
|
1124
1153
|
else if (f === "let") stack[stack.length - 1] = { ...top, names: { ...top.names } };
|
|
1125
1154
|
else stack[stack.length - 1] = { ...top };
|
|
1126
1155
|
}
|
|
1127
|
-
|
|
1156
|
+
const p = programOf(machine, options);
|
|
1157
|
+
// Each call has a new program. Thus the configuration holds the state of the evaluation that a later step needs.
|
|
1158
|
+
p.work = configuration.work ?? 0;
|
|
1159
|
+
for (const [at, count] of Object.entries(configuration.counts ?? {})) p.counts.set(at, count);
|
|
1160
|
+
// A list of effects from the host holds the earlier effects. Without it, the configuration holds their number.
|
|
1161
|
+
p.before = options.effects === undefined ? (configuration.effected ?? 0) : 0;
|
|
1162
|
+
const next = advance(p, { ...configuration, stack });
|
|
1163
|
+
const effected = p.before + p.effects.length;
|
|
1164
|
+
return {
|
|
1165
|
+
...next,
|
|
1166
|
+
...(p.work === 0 ? {} : { work: p.work }),
|
|
1167
|
+
...(p.counts.size === 0 ? {} : { counts: Object.fromEntries(p.counts) }),
|
|
1168
|
+
...(effected === 0 ? {} : { effected }),
|
|
1169
|
+
};
|
|
1128
1170
|
}
|
|
1129
1171
|
|
|
1130
1172
|
/**
|
|
@@ -1143,6 +1185,7 @@ export function stepper(machine: Machine, doc: unknown, context: Context = makeC
|
|
|
1143
1185
|
const p = programOf(machine, options);
|
|
1144
1186
|
try {
|
|
1145
1187
|
const end = drive(p, stepperStart(machine, doc, context, options));
|
|
1188
|
+
p.ended = true;
|
|
1146
1189
|
return end.result.ok ? ok(forHost(p, end, end.result.value)) : end.result;
|
|
1147
1190
|
} catch (error) {
|
|
1148
1191
|
return placed(thrown(error, "the stepper"), []);
|