@mark1russell7/coeval 0.23.0 → 0.24.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/README.md +4 -2
- package/build/index.d.ts +200 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +901 -79
- package/build/index.js.map +1 -1
- package/package.json +3 -3
- package/src/agree.ts +105 -2
- package/src/conform.ts +1 -1
- package/src/effects.ts +1 -1
- package/src/evaluate.ts +182 -8
- package/src/formula.ts +1 -0
- package/src/index.ts +10 -2
- package/src/reference.ts +55 -0
- package/src/schedule.ts +11 -2
- package/src/static.ts +61 -2
- package/src/stdlib.ts +89 -11
- package/src/syntax.ts +322 -20
- package/src/typing.ts +33 -3
- package/src/world.ts +7 -1
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.
|
|
4
|
+
"version": "0.24.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",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
],
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@mark1russell7/cue": "^2.1.1",
|
|
39
|
-
"@mark1russell7/rigor-bench": "^0.2.
|
|
40
|
-
"@mark1russell7/rigor-statistics": "^0.1.
|
|
39
|
+
"@mark1russell7/rigor-bench": "^0.2.29",
|
|
40
|
+
"@mark1russell7/rigor-statistics": "^0.1.16",
|
|
41
41
|
"@types/node": "^26.6.4",
|
|
42
42
|
"esbuild": "^0.28.2",
|
|
43
43
|
"typescript": "^7.0.2",
|
package/src/agree.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { type Key, docEquals, equalWith } from "./doc.ts";
|
|
2
2
|
import { type Context, isHostThing } from "./deref.ts";
|
|
3
|
-
import { type EvalError, type Result, isStackOverflow, thrown } from "./errors.ts";
|
|
4
|
-
import { type
|
|
3
|
+
import { type EvalError, type Result, describe, isStackOverflow, thrown } from "./errors.ts";
|
|
4
|
+
import { type CheckOptions, typeCheck } from "./static.ts";
|
|
5
|
+
import { isPrecise, member, typeText } from "./types.ts";
|
|
6
|
+
import { boundariesOf } from "./typing.ts";
|
|
7
|
+
import { type Effect, type Machine, type Perform, type Read, type TraceNode, JOURNAL, evaluate, makeContext, prepare, run, sharedCopy } from "./evaluate.ts";
|
|
5
8
|
import { Held, isClosure } from "./formula.ts";
|
|
6
9
|
import { type Definitions, reference, referenceRun } from "./reference.ts";
|
|
7
10
|
import { type ReplaySettle, recorder, relay, replayer } from "./replay.ts";
|
|
@@ -392,6 +395,15 @@ function resolutions(reads: readonly Read[]): readonly Read[] {
|
|
|
392
395
|
return reads.filter((read) => read.free && read.kept !== true);
|
|
393
396
|
}
|
|
394
397
|
|
|
398
|
+
/** This function gives the text of a value of a trace. It does not throw for a bigint or a record with a cycle. */
|
|
399
|
+
function valueText(value: unknown): string {
|
|
400
|
+
try {
|
|
401
|
+
return JSON.stringify(value) ?? describe(value);
|
|
402
|
+
} catch {
|
|
403
|
+
return describe(value);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
395
407
|
function pathText(path: readonly unknown[] | undefined): string {
|
|
396
408
|
if (path === undefined) return "no read";
|
|
397
409
|
try {
|
|
@@ -537,3 +549,94 @@ export async function agreeRun(machine: Machine, doc: unknown, context: Context
|
|
|
537
549
|
}
|
|
538
550
|
return compared === undefined ? { ok: true, result, effects: first.effects } : { ok: true, result, effects: first.effects, reads: compared };
|
|
539
551
|
}
|
|
552
|
+
|
|
553
|
+
/** The result of `agreeTypes`: the agreement of a check of types and a traced evaluation (STATIC.TYPES-AGREE). */
|
|
554
|
+
export type TypeAgreement =
|
|
555
|
+
| {
|
|
556
|
+
readonly ok: true;
|
|
557
|
+
/** `false` when the document has an issue, or a value breaks a position of an imprecise type. The rule then tells nothing. */
|
|
558
|
+
readonly checked: boolean;
|
|
559
|
+
/** The number of the values of the trace that the rule examined. */
|
|
560
|
+
readonly values: number;
|
|
561
|
+
}
|
|
562
|
+
| {
|
|
563
|
+
readonly ok: false;
|
|
564
|
+
/** `value`: a value is not in the type of its place. `argument`: it is not in the type that its position needs. `error`: a type error of the kernel. */
|
|
565
|
+
readonly clause: "value" | "argument" | "error";
|
|
566
|
+
/** The keys of the place, as texts. */
|
|
567
|
+
readonly place: readonly string[];
|
|
568
|
+
readonly reason: string;
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
/** The options of `agreeTypes`: the options of `check`, and `guards`. */
|
|
572
|
+
export interface AgreeTypesOptions extends CheckOptions {
|
|
573
|
+
/** `true`: the evaluation tests each boundary (PLAN.GUARD). The rule then holds for each context. The default is `false`. */
|
|
574
|
+
readonly guards?: boolean;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/** The type errors of the kernel, by the evaluator that gives them (STATIC.TYPES-AGREE). */
|
|
578
|
+
const TYPE_ERRORS: Readonly<Record<string, string>> = {
|
|
579
|
+
"formula.op": "#NAME?",
|
|
580
|
+
"formula.if": "#VALUE!",
|
|
581
|
+
"formula.and": "#VALUE!",
|
|
582
|
+
"formula.or": "#VALUE!",
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* This function examines the agreement rule for types (STATIC.TYPES-AGREE, D148). A document with no issue evaluates
|
|
587
|
+
* with a trace. Each value of the trace is a member of the type of its place. A value of a precise type is also a member
|
|
588
|
+
* of the type that its position needs. No document with precise parts gives a type error of the kernel.
|
|
589
|
+
*
|
|
590
|
+
* A language imports this function for its own property tests, with `agree`. A failure tells that a signature, a type
|
|
591
|
+
* rule, a cover or a root type does not hold. A value that breaks a position of an imprecise type ends the promise, and
|
|
592
|
+
* the result then has `checked: false` (TYPE.GRADUAL).
|
|
593
|
+
*
|
|
594
|
+
* @param machine - The world and the code of the evaluators, with their type rules.
|
|
595
|
+
* @param doc - The document.
|
|
596
|
+
* @param context - The context. Its roots are members of the root types.
|
|
597
|
+
* @param options - The options of `check`, with the root types, and `guards`.
|
|
598
|
+
* @returns The agreement, or the first place that breaks it.
|
|
599
|
+
*/
|
|
600
|
+
export function agreeTypes(machine: Machine, doc: unknown, context: Context = makeContext(), options: AgreeTypesOptions = {}): TypeAgreement {
|
|
601
|
+
const typing = typeCheck(machine, doc, options);
|
|
602
|
+
if (typing.issues.length > 0) return { ok: true, checked: false, values: 0 };
|
|
603
|
+
const keyOf = (path: readonly unknown[]): string => JSON.stringify(path.map(String));
|
|
604
|
+
const places = new Map(typing.places.map((item) => [keyOf(item.place), item]));
|
|
605
|
+
const domains = machine.domains;
|
|
606
|
+
const guards = options.guards === true ? boundariesOf(typing, domains) : undefined;
|
|
607
|
+
const given = { ...(options.declaredType === undefined ? {} : { declaredType: options.declaredType }), ...(guards === undefined ? {} : { guards }) };
|
|
608
|
+
const stack: TraceNode[] = [evaluate(machine, doc, context, given).trace];
|
|
609
|
+
const nodes: TraceNode[] = [];
|
|
610
|
+
while (stack.length > 0) {
|
|
611
|
+
const node = stack.pop() as TraceNode;
|
|
612
|
+
nodes.push(node);
|
|
613
|
+
stack.push(...node.children);
|
|
614
|
+
}
|
|
615
|
+
// A value that breaks a position of an imprecise type leaves the promise of each later type (K28).
|
|
616
|
+
for (const node of nodes) {
|
|
617
|
+
const place = places.get(keyOf(node.path));
|
|
618
|
+
if (place?.expected === undefined || !node.result.ok || isPrecise(place.type, domains)) continue;
|
|
619
|
+
if (!member(node.result.value, place.expected, domains)) return { ok: true, checked: false, values: 0 };
|
|
620
|
+
}
|
|
621
|
+
// The walk goes from the end, thus a part comes before the kind that reads it, and a finding names the origin.
|
|
622
|
+
let values = 0;
|
|
623
|
+
for (const node of nodes.reverse()) {
|
|
624
|
+
const place = places.get(keyOf(node.path));
|
|
625
|
+
if (place === undefined) continue;
|
|
626
|
+
const at = node.path.map(String);
|
|
627
|
+
if (!node.result.ok) {
|
|
628
|
+
const parts = node.children.map((child) => places.get(keyOf(child.path)));
|
|
629
|
+
const decided = node.children.every((child) => child.result.ok) && parts.every((part) => part?.expected !== undefined && isPrecise(part.type, domains));
|
|
630
|
+
const code = TYPE_ERRORS[node.answer.evaluator ?? ""];
|
|
631
|
+
if (decided && node.result.error.code === code) return { ok: false, clause: "error", place: at, reason: `The document gives ${code}, but it checks with no issue.` };
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
const value = node.result.value;
|
|
635
|
+
values += 1;
|
|
636
|
+
if (!member(value, place.type, domains)) return { ok: false, clause: "value", place: at, reason: `The value ${valueText(value)} is not a member of ${typeText(place.type)}.` };
|
|
637
|
+
if (place.expected !== undefined && !member(value, place.expected, domains)) {
|
|
638
|
+
return { ok: false, clause: "argument", place: at, reason: `The value ${valueText(value)} is not a member of the needed type ${typeText(place.expected)}.` };
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return { ok: true, checked: true, values };
|
|
642
|
+
}
|
package/src/conform.ts
CHANGED
|
@@ -867,7 +867,7 @@ function instanceOf(env: Env, under: Under, doc: Envelope, split: SplitOk, sampl
|
|
|
867
867
|
const fn = under.fn;
|
|
868
868
|
return (shot) => {
|
|
869
869
|
const ctx = shot.context;
|
|
870
|
-
const parts: RunPart[] = split.parts.map((slot, index) => ({ ...handleOf(slot), evaluate: (given?: Context) => shot.steps(index, "evaluate", given ?? ctx) }));
|
|
870
|
+
const parts: RunPart[] = split.parts.map((slot, index) => ({ ...handleOf(slot), evaluate: (given?: Context) => shot.steps(index, "evaluate", given ?? ctx), handled: () => shot.steps(index, "evaluate", ctx) }));
|
|
871
871
|
const input: RunInput = {
|
|
872
872
|
...base,
|
|
873
873
|
value: fill(parts),
|
package/src/effects.ts
CHANGED
|
@@ -46,7 +46,7 @@ function reaching(machine: Machine, doc: unknown, options: EffectsOptions): read
|
|
|
46
46
|
doc,
|
|
47
47
|
(v) => {
|
|
48
48
|
const caps = v.caps ?? EACH;
|
|
49
|
-
for (const kind of declaredEffects(v, "eval", machine)) if (rule(caps, kind)) out.push({ kind, visit: v });
|
|
49
|
+
for (const kind of declaredEffects(v, "eval", machine)) if (rule(caps, kind) && (v.handled === undefined || !permits(v.handled, kind))) out.push({ kind, visit: v });
|
|
50
50
|
return undefined;
|
|
51
51
|
},
|
|
52
52
|
{ caps: options.caps ?? EACH, ...(options.declaredType === undefined ? {} : { declaredType: options.declaredType }) },
|
package/src/evaluate.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { type Envelope, type Json, type Key, type TypeRef, isEnvelope, isKey, ty
|
|
|
2
2
|
import { type PartSlot, type Split, partsOf, pathMatches, scopesOf } from "./parts.ts";
|
|
3
3
|
import { answerOf, ask, type Answer } from "./ladder.ts";
|
|
4
4
|
import type { CapsRule, Kind, KindFacts, Side } from "./world.ts";
|
|
5
|
-
import type
|
|
6
|
-
import type { TypeRule } from "./typing.ts";
|
|
5
|
+
import { type Domain, type ValueType, member, typeText } from "./types.ts";
|
|
6
|
+
import type { Boundary, TypeRule } from "./typing.ts";
|
|
7
7
|
import type { StaticRead } from "./facts.ts";
|
|
8
8
|
import { type ErrorCode, type EvalError, type Result, Failure, fail, ok, placed, thrown, describe } from "./errors.ts";
|
|
9
9
|
import {
|
|
@@ -109,7 +109,7 @@ export function intersectCaps(a: readonly Cap[], b: readonly Cap[]): readonly Ca
|
|
|
109
109
|
/** This function gives the capability rules of a kind that cover a part and change its capabilities. */
|
|
110
110
|
function rulesFor(rules: readonly CapsRule[] | undefined, path: readonly Key[]): readonly CapsRule[] {
|
|
111
111
|
if (rules === undefined || rules.length === 0) return NO_RULES;
|
|
112
|
-
const out = rules.filter((rule) => rule.rule !== "same" && (rule.in === undefined || rule.in.some((pattern) => pathMatches(path, pattern))));
|
|
112
|
+
const out = rules.filter((rule) => rule.rule !== "same" && rule.rule !== "grant" && (rule.in === undefined || rule.in.some((pattern) => pathMatches(path, pattern))));
|
|
113
113
|
return out.length === 0 ? NO_RULES : out;
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -136,6 +136,87 @@ export function partCaps(rules: readonly CapsRule[] | undefined, path: readonly
|
|
|
136
136
|
return capsUnder(rulesFor(rules, path), caps);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
/** This function collects the values at a path in a value. The key `"*"` steps into each item of a list. */
|
|
140
|
+
function collectAt(value: unknown, path: readonly Key[], index: number, out: Cap[]): void {
|
|
141
|
+
if (index === path.length) {
|
|
142
|
+
if (typeof value === "string" || (Array.isArray(value) && value.every((key) => typeof key === "string" || typeof key === "number"))) out.push(value as Cap);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const key = path[index] as Key;
|
|
146
|
+
if (key === "*") {
|
|
147
|
+
if (Array.isArray(value)) for (const item of value) collectAt(item, path, index + 1, out);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (typeof value === "object" && value !== null) collectAt((value as Record<Key, unknown>)[key], path, index + 1, out);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* This function gives the capabilities that the rules `grant` of a kind give one part (CAP.GRANT). The check and the
|
|
155
|
+
* walk read them from the document. The kernel gets the same list from the clauses of the runner.
|
|
156
|
+
*
|
|
157
|
+
* @param rules - The capability rules of the kind (`Kind.caps`).
|
|
158
|
+
* @param path - The path of the part in the value of the document.
|
|
159
|
+
* @param value - The value of the document.
|
|
160
|
+
* @returns The capabilities that the rules add, or an empty list.
|
|
161
|
+
*/
|
|
162
|
+
export function partGrants(rules: readonly CapsRule[] | undefined, path: readonly Key[], value: unknown): readonly Cap[] {
|
|
163
|
+
if (rules === undefined) return NO_CAPS;
|
|
164
|
+
let out: Cap[] | undefined;
|
|
165
|
+
for (const rule of rules) {
|
|
166
|
+
if (rule.rule !== "grant" || (rule.in !== undefined && !rule.in.some((pattern) => pathMatches(path, pattern)))) continue;
|
|
167
|
+
collectAt(value, rule.from, 0, (out ??= []));
|
|
168
|
+
}
|
|
169
|
+
return out ?? NO_CAPS;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* One clause of a handler (D146): the effects under `op` go to `answer` and not to the host. The clause runs in place,
|
|
174
|
+
* as a part of the effect that it answers. Its result is the result of that effect.
|
|
175
|
+
*/
|
|
176
|
+
export interface Clause {
|
|
177
|
+
/** The kind of the effects: the clause answers this kind and each kind under it (CAP.KEYS). */
|
|
178
|
+
readonly op: Cap;
|
|
179
|
+
/**
|
|
180
|
+
* This function answers one effect. It sees only the payload (HANDLE.SEALED).
|
|
181
|
+
*
|
|
182
|
+
* @param payload - The data of the effect.
|
|
183
|
+
* @returns The steps that give the result of the effect.
|
|
184
|
+
*/
|
|
185
|
+
answer(payload: unknown): Steps;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** The clauses of one active `handle`, and the frame around it. */
|
|
189
|
+
interface Frame {
|
|
190
|
+
readonly clauses: readonly Clause[];
|
|
191
|
+
readonly outer: Frame | undefined;
|
|
192
|
+
/** The runtime of the evaluation that started the frame. A frame of another evaluation has ended. */
|
|
193
|
+
readonly owner: Runtime;
|
|
194
|
+
/** `true` after the body ends. A closure can leave the body with the context of the frame (HANDLE.ENDED). */
|
|
195
|
+
ended: boolean;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The key of the frame of a context. Like the grant, it stays through a spread of a context, and only the kernel writes
|
|
200
|
+
* it. It is not a root (D10): a document cannot read it (HANDLE.SEALED).
|
|
201
|
+
*/
|
|
202
|
+
const FRAME = /* @__PURE__ */ Symbol("coeval.frame");
|
|
203
|
+
|
|
204
|
+
/** A context that can have a frame. */
|
|
205
|
+
type Framed = Context & { readonly [FRAME]?: Frame };
|
|
206
|
+
|
|
207
|
+
/** This function gives the frame of a context, when it has one. */
|
|
208
|
+
function frameOf(ctx: Context): Frame | undefined {
|
|
209
|
+
return (ctx as Framed)[FRAME];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** This function finds the frame and the clause that answer an effect. The innermost frame wins, then the first clause. */
|
|
213
|
+
function clauseFor(start: Frame, keys: readonly Key[]): { readonly frame: Frame; readonly clause: Clause } | undefined {
|
|
214
|
+
for (let frame: Frame | undefined = start; frame !== undefined; frame = frame.outer) {
|
|
215
|
+
for (const clause of frame.clauses) if (permits([clause.op], keys)) return { frame, clause };
|
|
216
|
+
}
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
|
|
139
220
|
/**
|
|
140
221
|
* The key of the grant of a context (CAP.NARROW-CODE): the capabilities that the kernel gave the document of the
|
|
141
222
|
* context. A spread of a context keeps its grant. Thus compiled code passes the grant on in each context that it makes
|
|
@@ -669,6 +750,14 @@ export interface RunPart {
|
|
|
669
750
|
* @returns The steps of the evaluation of the part.
|
|
670
751
|
*/
|
|
671
752
|
evaluate(context?: Context): Steps;
|
|
753
|
+
/**
|
|
754
|
+
* This function evaluates the part under clauses that answer its effects (D146). The kernel sets it on each part of
|
|
755
|
+
* a kind with a rule `grant`. A part that a runner makes itself has none, and a compiled runner gets no such part.
|
|
756
|
+
*
|
|
757
|
+
* @param clauses - The clauses, in order. An effect goes to the first clause that names its kind.
|
|
758
|
+
* @returns The steps of the evaluation of the part.
|
|
759
|
+
*/
|
|
760
|
+
handled?(clauses: readonly Clause[]): Steps;
|
|
672
761
|
}
|
|
673
762
|
|
|
674
763
|
/** The input of a runner: the input of an evaluator, with parts and effects that can wait. */
|
|
@@ -904,6 +993,8 @@ export interface PlanRunOptions {
|
|
|
904
993
|
export interface RunOptions extends PlanRunOptions {
|
|
905
994
|
/** The type of a bare root value. */
|
|
906
995
|
readonly declaredType?: string;
|
|
996
|
+
/** The boundaries that test their values (PLAN.GUARD). The default is none. */
|
|
997
|
+
readonly guards?: readonly Boundary[];
|
|
907
998
|
/** The verb of `run`, for example `format`. The default is `eval` (VERB.PREPARE). */
|
|
908
999
|
readonly verb?: string;
|
|
909
1000
|
}
|
|
@@ -934,6 +1025,8 @@ export interface EvaluateOptions {
|
|
|
934
1025
|
readonly perform?: Perform;
|
|
935
1026
|
/** A list that gets each read, in the order of the resolutions (READS.RECORD). */
|
|
936
1027
|
readonly reads?: Read[];
|
|
1028
|
+
/** The boundaries that test their values (PLAN.GUARD). The default is none. */
|
|
1029
|
+
readonly guards?: readonly Boundary[];
|
|
937
1030
|
}
|
|
938
1031
|
|
|
939
1032
|
/** The options of a resolution without a record of hops, and with one. One object of each serves each call. */
|
|
@@ -1288,14 +1381,35 @@ class Session implements Runtime {
|
|
|
1288
1381
|
return (effect.outcome = fail("#CALC!", `The mock gave a promise for the effect ${effect.kind}, and an evaluation does not wait.`, { place }));
|
|
1289
1382
|
}
|
|
1290
1383
|
|
|
1384
|
+
/**
|
|
1385
|
+
* This function finds the clause that answers an effect (D146). Without it, the effect goes on to the host. An effect
|
|
1386
|
+
* without a capability gets no clause, thus `record` gives its `#CAP!` (HANDLE.NARROW). A clause of a frame that has
|
|
1387
|
+
* ended gives `#CAP!`, because the capability came from that frame (HANDLE.ENDED).
|
|
1388
|
+
*/
|
|
1389
|
+
private clauseOf(given: Context, start: Frame, kind: Cap, place: readonly Key[]): Clause | Result | undefined {
|
|
1390
|
+
const ctx = inEpoch(given, this.epoch) ? granted(given) : ungranted(given, this.epoch);
|
|
1391
|
+
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
1392
|
+
if (!this.permitted(ctx, keys)) return undefined;
|
|
1393
|
+
const found = clauseFor(start, keys);
|
|
1394
|
+
if (found === undefined) return undefined;
|
|
1395
|
+
if (found.frame.ended || found.frame.owner !== this) return fail("#CAP!", `The handler of the effect ${keys.join(".")} has ended.`, { place });
|
|
1396
|
+
return found.clause;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1291
1399
|
effect(ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result {
|
|
1292
1400
|
const text = typeof kind === "string" ? kind : kind.join(".");
|
|
1293
1401
|
if (this.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
|
|
1402
|
+
const frame = frameOf(ctx);
|
|
1403
|
+
const clause = frame === undefined ? undefined : this.clauseOf(ctx, frame, kind, place);
|
|
1404
|
+
if (clause !== undefined) return "ok" in clause ? clause : drive(clause.answer(payload), text);
|
|
1294
1405
|
const made = this.record(ctx, kind, payload, place);
|
|
1295
1406
|
return "ok" in made ? made : this.answer(made, place);
|
|
1296
1407
|
}
|
|
1297
1408
|
|
|
1298
1409
|
*runEffect(ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Steps {
|
|
1410
|
+
const frame = frameOf(ctx);
|
|
1411
|
+
const clause = frame === undefined ? undefined : this.clauseOf(ctx, frame, kind, place);
|
|
1412
|
+
if (clause !== undefined) return "ok" in clause ? clause : yield* clause.answer(payload);
|
|
1299
1413
|
const made = this.record(ctx, kind, payload, place);
|
|
1300
1414
|
if ("ok" in made) return made;
|
|
1301
1415
|
const perform = this.perform;
|
|
@@ -1590,6 +1704,11 @@ export interface PrepareOptions {
|
|
|
1590
1704
|
* the code without the fold. A plan without folds ignores this option.
|
|
1591
1705
|
*/
|
|
1592
1706
|
readonly fixed?: { readonly names?: Readonly<Record<string, unknown>> };
|
|
1707
|
+
/**
|
|
1708
|
+
* The boundaries of the document, from `boundariesOf` (PLAN.GUARD, D149). A value that is not a member of the type of
|
|
1709
|
+
* its boundary gives `#VALUE!` at that place. The default is none, and a plan then has no test.
|
|
1710
|
+
*/
|
|
1711
|
+
readonly guards?: readonly Boundary[];
|
|
1593
1712
|
}
|
|
1594
1713
|
|
|
1595
1714
|
/** A compiled document. It evaluates many times, and it does the work of the ladder one time. */
|
|
@@ -1630,6 +1749,8 @@ interface Compiler {
|
|
|
1630
1749
|
readonly fold: boolean;
|
|
1631
1750
|
/** The fixed names and their values, or `undefined` when the plan has none (PLAN.FIXED). */
|
|
1632
1751
|
readonly fixed: Readonly<Record<string, unknown>> | undefined;
|
|
1752
|
+
/** The types of the boundaries by their place, or `undefined` when the plan has no guard (PLAN.GUARD). */
|
|
1753
|
+
readonly guards: ReadonlyMap<string, ValueType> | undefined;
|
|
1633
1754
|
/** The runtime of the folds of this compilation. The first fold makes it. */
|
|
1634
1755
|
probe?: Probe;
|
|
1635
1756
|
}
|
|
@@ -1771,11 +1892,12 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
|
|
|
1771
1892
|
if (runner !== undefined) {
|
|
1772
1893
|
let inner: RunCode;
|
|
1773
1894
|
try {
|
|
1774
|
-
inner = isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill);
|
|
1895
|
+
inner = isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill, kind?.caps?.some((rule) => rule.rule === "grant") === true);
|
|
1775
1896
|
} catch (error) {
|
|
1776
1897
|
return failingWith(c, meta, placed(thrown(error, `the runner ${name}`), place));
|
|
1777
1898
|
}
|
|
1778
|
-
const
|
|
1899
|
+
const guard = guardOf(c, place);
|
|
1900
|
+
const run = placedRun(guard === undefined ? inner : guardRun(inner, guard, c.machine.domains), place, name);
|
|
1779
1901
|
return finish(c, meta, runnerCode(run, place, kindText), run);
|
|
1780
1902
|
}
|
|
1781
1903
|
|
|
@@ -1785,8 +1907,9 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
|
|
|
1785
1907
|
} catch (error) {
|
|
1786
1908
|
return failingWith(c, meta, placed(thrown(error, `the evaluator ${name}`), place));
|
|
1787
1909
|
}
|
|
1910
|
+
const guard = guardOf(c, place);
|
|
1788
1911
|
// Code that cannot fail needs no place and no catch, and it does not read the context of its grant (PLAN.TOTAL).
|
|
1789
|
-
const done = finish(c, meta, TOTAL.has(inner) ? inner : placedCode(inner, place, name), undefined);
|
|
1912
|
+
const done = finish(c, meta, guard !== undefined ? placedCode(guardCode(inner, guard, c.machine.domains), place, name) : TOTAL.has(inner) ? inner : placedCode(inner, place, name), undefined);
|
|
1790
1913
|
if (frees === undefined || kind === undefined || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
|
|
1791
1914
|
const free = freeOf(doc, kind, split.parts, frees);
|
|
1792
1915
|
if (free === undefined) return done;
|
|
@@ -2198,6 +2321,34 @@ function gated(rules: readonly CapsRule[], part: Compiled): Compiled {
|
|
|
2198
2321
|
* This function gives the steps of a runner, with the place of each error and of each exception. The steps get the
|
|
2199
2322
|
* context that its grant permits (CAP.NARROW-CODE).
|
|
2200
2323
|
*/
|
|
2324
|
+
/** This function gives the key of a place in the guards of a plan. */
|
|
2325
|
+
function guardKey(place: readonly Key[]): string {
|
|
2326
|
+
return JSON.stringify(place.map(String));
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
/** This function gives the type of the boundary at a place, when the plan has one (PLAN.GUARD). */
|
|
2330
|
+
function guardOf(c: Compiler, place: readonly Key[]): ValueType | undefined {
|
|
2331
|
+
return c.guards?.get(guardKey(place));
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
/** This function gives the result of a part of a boundary: a value that is not a member gives `#VALUE!` (PLAN.GUARD). */
|
|
2335
|
+
function guarded(result: Result, type: ValueType, domains: Readonly<Record<string, Domain>> | undefined): Result {
|
|
2336
|
+
if (!result.ok || member(result.value, type, domains)) return result;
|
|
2337
|
+
return fail("#VALUE!", `The value is not a member of ${typeText(type)}, the type of its boundary.`);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
/** This function adds the test of a boundary to the code of a part. */
|
|
2341
|
+
function guardCode(inner: Code, type: ValueType, domains: Readonly<Record<string, Domain>> | undefined): Code {
|
|
2342
|
+
return (ctx, rt) => guarded(inner(ctx, rt), type, domains);
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
/** This function adds the test of a boundary to the steps of a part. */
|
|
2346
|
+
function guardRun(inner: RunCode, type: ValueType, domains: Readonly<Record<string, Domain>> | undefined): RunCode {
|
|
2347
|
+
return function* (ctx, rt) {
|
|
2348
|
+
return guarded(yield* inner(ctx, rt), type, domains);
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2201
2352
|
function placedRun(inner: RunCode, place: readonly Key[], name: string): RunCode {
|
|
2202
2353
|
return function* (ctx, rt) {
|
|
2203
2354
|
try {
|
|
@@ -2316,7 +2467,25 @@ function interpret(fn: EvalFn, input: CompileInput, fill: <P>(handles: readonly
|
|
|
2316
2467
|
};
|
|
2317
2468
|
}
|
|
2318
2469
|
|
|
2319
|
-
|
|
2470
|
+
/**
|
|
2471
|
+
* This function runs a part under a new frame (D146). The part gets the capabilities of the document and the kinds of
|
|
2472
|
+
* the clauses (CAP.GRANT), with the grant of the kernel. A rule around an effect narrows them as it narrows any other
|
|
2473
|
+
* capability (HANDLE.NARROW). The frame ends with the steps.
|
|
2474
|
+
*/
|
|
2475
|
+
function* handling(given: Context, clauses: readonly Clause[], rt: Runtime, run: (ctx: Context) => Steps): Steps {
|
|
2476
|
+
const ctx = granted(given);
|
|
2477
|
+
const frame: Frame = { clauses, outer: frameOf(ctx), owner: rt, ended: false };
|
|
2478
|
+
const caps = [...ctx.caps];
|
|
2479
|
+
for (const clause of clauses) if (!permits(caps, clause.op)) caps.push(clause.op);
|
|
2480
|
+
const inner: Granted & Framed = { ...ctx, caps, [GRANT]: caps, [EPOCH]: epochOf(ctx), [FRAME]: frame };
|
|
2481
|
+
try {
|
|
2482
|
+
return yield* run(inner);
|
|
2483
|
+
} finally {
|
|
2484
|
+
frame.ended = true;
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonly P[]) => unknown, grants: boolean): RunCode {
|
|
2320
2489
|
const { kind, typePath, doc, data, place, machine } = input;
|
|
2321
2490
|
const refill = input.fill;
|
|
2322
2491
|
const compiled = input.parts;
|
|
@@ -2327,6 +2496,8 @@ function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonl
|
|
|
2327
2496
|
path: part.path,
|
|
2328
2497
|
...(part.name === undefined ? {} : { name: part.name }),
|
|
2329
2498
|
evaluate: (override?: Context) => part.run(override === undefined ? ctx : handed(ctx, override), rt),
|
|
2499
|
+
// Only a kind with a rule grant pays for this function: most runners stay as fast as before.
|
|
2500
|
+
...(grants ? { handled: (clauses: readonly Clause[]) => handling(ctx, clauses, rt, (inner) => part.run(inner, rt)) } : {}),
|
|
2330
2501
|
}));
|
|
2331
2502
|
return fn({
|
|
2332
2503
|
kind,
|
|
@@ -2390,7 +2561,8 @@ export function prepare(machine: Machine, doc: unknown, options: PrepareOptions
|
|
|
2390
2561
|
const traced = options.trace === true;
|
|
2391
2562
|
const fold = !traced && options.fold !== false;
|
|
2392
2563
|
const fixed = fold ? fixedNamesOf(options.fixed?.names) : undefined;
|
|
2393
|
-
const
|
|
2564
|
+
const guards = options.guards === undefined || options.guards.length === 0 ? undefined : new Map(options.guards.map((each) => [guardKey(each.place), each.type]));
|
|
2565
|
+
const c: Compiler = { machine, traced, maxDepth: machine.maxDepth ?? 1000, verb, noAnswer, fold, fixed, guards };
|
|
2394
2566
|
let root: Compiled;
|
|
2395
2567
|
try {
|
|
2396
2568
|
root = compileNode(c, doc, [], options.declaredType, 0);
|
|
@@ -2513,6 +2685,7 @@ export function evaluate(machine: Machine, doc: unknown, context: Context = make
|
|
|
2513
2685
|
trace: true,
|
|
2514
2686
|
...(declaredType === undefined ? {} : { declaredType }),
|
|
2515
2687
|
...(given.verb === undefined ? {} : { verb: given.verb }),
|
|
2688
|
+
...(given.guards === undefined ? {} : { guards: given.guards }),
|
|
2516
2689
|
});
|
|
2517
2690
|
const ctx = rootContext(machine, context);
|
|
2518
2691
|
const s = new Session(machine, ctx.caps, false, true, given.perform, undefined, undefined, readingOf(given.reads, ctx));
|
|
@@ -2535,6 +2708,7 @@ export async function run(machine: Machine, doc: unknown, context: Context = mak
|
|
|
2535
2708
|
const plan = prepare(machine, doc, {
|
|
2536
2709
|
trace: true,
|
|
2537
2710
|
...(options.declaredType === undefined ? {} : { declaredType: options.declaredType }),
|
|
2711
|
+
...(options.guards === undefined ? {} : { guards: options.guards }),
|
|
2538
2712
|
...(options.verb === undefined ? {} : { verb: options.verb }),
|
|
2539
2713
|
});
|
|
2540
2714
|
const ctx = rootContext(machine, context);
|
package/src/formula.ts
CHANGED
|
@@ -420,6 +420,7 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
|
|
|
420
420
|
"formula.lit": { synth: (input) => valueType(input.doc.value) },
|
|
421
421
|
|
|
422
422
|
"formula.ref": {
|
|
423
|
+
read: true,
|
|
423
424
|
synth: (input, report) => {
|
|
424
425
|
const path = input.doc.value;
|
|
425
426
|
// An address gives a thing, not a value. The check gives #VALUE! for a path that is not a list.
|
package/src/index.ts
CHANGED
|
@@ -161,6 +161,7 @@ export {
|
|
|
161
161
|
type RunFn,
|
|
162
162
|
type RunInput,
|
|
163
163
|
type RunOptions,
|
|
164
|
+
type Clause,
|
|
164
165
|
type RunPart,
|
|
165
166
|
type Runner,
|
|
166
167
|
type Runtime,
|
|
@@ -177,6 +178,7 @@ export {
|
|
|
177
178
|
narrow,
|
|
178
179
|
nodeAt,
|
|
179
180
|
partCaps,
|
|
181
|
+
partGrants,
|
|
180
182
|
permits,
|
|
181
183
|
planner,
|
|
182
184
|
prepare,
|
|
@@ -195,6 +197,7 @@ export {
|
|
|
195
197
|
STD_EVALUATORS,
|
|
196
198
|
STD_MACHINE,
|
|
197
199
|
STD_RUNNERS,
|
|
200
|
+
ifRunner,
|
|
198
201
|
STD_WALKERS,
|
|
199
202
|
STD_WORLD,
|
|
200
203
|
WORLD_SYNTAX,
|
|
@@ -225,6 +228,7 @@ export {
|
|
|
225
228
|
} from "./context.ts";
|
|
226
229
|
export {
|
|
227
230
|
type CheckOptions,
|
|
231
|
+
type DocChange,
|
|
228
232
|
type DocVisit,
|
|
229
233
|
type Issue,
|
|
230
234
|
type RefUse,
|
|
@@ -235,6 +239,7 @@ export {
|
|
|
235
239
|
type WorldOptions,
|
|
236
240
|
check,
|
|
237
241
|
checkWorld,
|
|
242
|
+
mapDocs,
|
|
238
243
|
mapRefs,
|
|
239
244
|
refsOf,
|
|
240
245
|
typeCheck,
|
|
@@ -267,6 +272,8 @@ export {
|
|
|
267
272
|
type SyntaxCalls,
|
|
268
273
|
type SyntaxConstructs,
|
|
269
274
|
type SyntaxEnvelopes,
|
|
275
|
+
type SyntaxFinding,
|
|
276
|
+
type SyntaxFindingCode,
|
|
270
277
|
type SyntaxFixity,
|
|
271
278
|
type SyntaxForm,
|
|
272
279
|
type SyntaxFormArgs,
|
|
@@ -279,12 +286,13 @@ export {
|
|
|
279
286
|
type SyntaxRefs,
|
|
280
287
|
MAX_SYNTAX_ISSUES,
|
|
281
288
|
STD_SYNTAX,
|
|
289
|
+
checkSyntax,
|
|
282
290
|
parse,
|
|
283
291
|
print,
|
|
284
292
|
syntaxFormat,
|
|
285
293
|
} from "./syntax.ts";
|
|
286
294
|
export { type Definitions, type ReferenceOptions, type ReferenceRunOptions, STD_DEFINITIONS, formulaDefinitions, reference, referenceRun } from "./reference.ts";
|
|
287
|
-
export { type AgreeOptions, type AgreeRunOptions, type Agreement, type Tier, type TierInput, type TierResult, agree, agreeRun, sameValue } from "./agree.ts";
|
|
295
|
+
export { type AgreeOptions, type AgreeRunOptions, type Agreement, type AgreeTypesOptions, type TypeAgreement, agreeTypes, type Tier, type TierInput, type TierResult, agree, agreeRun, sameValue } from "./agree.ts";
|
|
288
296
|
export { type ConformOptions, type ConformProbe, type Finding, type Obligation, conforms } from "./conform.ts";
|
|
289
297
|
export { sameResult } from "./same.ts";
|
|
290
298
|
export { type PrintOptions, type ReadPrints, type ReadsCheck, type VerifyOptions, printReads, verifyReads } from "./reads.ts";
|
|
@@ -341,5 +349,5 @@ export {
|
|
|
341
349
|
} from "./replay.ts";
|
|
342
350
|
export { type GenerateOptions, type Random, generate, random, typesOf } from "./generate.ts";
|
|
343
351
|
export { type Domain, type DomainCodec, type TypeIssue, type ValueType, STD_DOMAINS, fits, join, member, typeIssues, typeText } from "./types.ts";
|
|
344
|
-
export { type Declared, type PartType, type RootTypes, type TypeInput, type TypeProblem, type TypeRule, type Typed, CAPS_TYPE } from "./typing.ts";
|
|
352
|
+
export { type Boundary, type Declared, type PartType, type RootTypes, type TypeInput, type TypeProblem, type TypeRule, type Typed, CAPS_TYPE, boundariesOf } from "./typing.ts";
|
|
345
353
|
export { type SampleOptions, type TypeReading, typeFromSamples, typeFromSchema } from "./providers.ts";
|
package/src/reference.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { type AttributeRung, AT_READERS, STD_EVALUATORS } from "./stdlib.ts";
|
|
|
21
21
|
import { type EvalError, type Failed, type Result, Failure, describe, fail, ok, placed, thrown } from "./errors.ts";
|
|
22
22
|
import {
|
|
23
23
|
type AllOptions,
|
|
24
|
+
type Clause,
|
|
24
25
|
type Effect,
|
|
25
26
|
type EvalFn,
|
|
26
27
|
type EvalInput,
|
|
@@ -295,6 +296,44 @@ function freeIn(root: Context, ctx: Context, path: readonly unknown[]): boolean
|
|
|
295
296
|
return bound === Object.hasOwn(root.names, key) && (!bound || Object.is(ctx.names[key], root.names[key]));
|
|
296
297
|
}
|
|
297
298
|
|
|
299
|
+
/** The clauses of one active `handle`, as SPEC tells: the frame around it, and if its body has ended. */
|
|
300
|
+
interface RefFrame {
|
|
301
|
+
readonly clauses: readonly Clause[];
|
|
302
|
+
readonly outer: RefFrame | undefined;
|
|
303
|
+
/** The list of the effects of the evaluation: a frame of another evaluation has ended. */
|
|
304
|
+
readonly owner: unknown;
|
|
305
|
+
ended: boolean;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** The key of the frame of a context. */
|
|
309
|
+
const REF_FRAME = /* @__PURE__ */ Symbol("coeval.reference.frame");
|
|
310
|
+
|
|
311
|
+
/** This function gives the frame of a context. */
|
|
312
|
+
function refFrame(ctx: Context): RefFrame | undefined {
|
|
313
|
+
return (ctx as Context & { readonly [REF_FRAME]?: RefFrame })[REF_FRAME];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* This function finds the clause for an effect (HANDLE.DO, HANDLE.FORWARD). An effect without a capability gets no clause
|
|
318
|
+
* (HANDLE.NARROW). A clause of a frame that has ended gives `#CAP!` (HANDLE.ENDED).
|
|
319
|
+
*/
|
|
320
|
+
function clauseAt(env: Env, ctx: Context, kind: Cap, place: readonly Key[]): Clause | Failed | undefined {
|
|
321
|
+
let frame = refFrame(ctx);
|
|
322
|
+
if (frame === undefined) return undefined;
|
|
323
|
+
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
324
|
+
const machine = env.machine;
|
|
325
|
+
const allowed = machine.permits === undefined ? permits(ctx.caps, keys) : machine.permits(ctx.caps, keys, ctx);
|
|
326
|
+
if (!allowed) return undefined;
|
|
327
|
+
for (; frame !== undefined; frame = frame.outer) {
|
|
328
|
+
for (const clause of frame.clauses) {
|
|
329
|
+
if (!permits([clause.op], keys)) continue;
|
|
330
|
+
if (frame.ended || frame.owner !== env.effects) return fail("#CAP!", `The handler of the effect ${keys.join(".")} has ended.`, { place });
|
|
331
|
+
return clause;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
|
|
298
337
|
/**
|
|
299
338
|
* This function causes an effect when the capabilities permit it (RUN.CAP, CAP.HOOK). It records the effect with its
|
|
300
339
|
* address (RUN.ADDRESS) and its outcome (RUN.OUTCOME). The mock answers the effect at once (RUN.MOCK). Without a
|
|
@@ -314,6 +353,8 @@ function cause(env: Env, ctx: Context, kind: Cap, payload: unknown, place: reado
|
|
|
314
353
|
function causeFromEvaluator(env: Env, ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result {
|
|
315
354
|
const text = typeof kind === "string" ? kind : kind.join(".");
|
|
316
355
|
if (env.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
|
|
356
|
+
const clause = clauseAt(env, ctx, kind, place);
|
|
357
|
+
if (clause !== undefined) return "ok" in clause ? clause : drive(clause.answer(payload), place, text);
|
|
317
358
|
return cause(env, ctx, kind, payload, place);
|
|
318
359
|
}
|
|
319
360
|
|
|
@@ -323,6 +364,8 @@ function causeFromEvaluator(env: Env, ctx: Context, kind: Cap, payload: unknown,
|
|
|
323
364
|
* (RUN.OUTCOME).
|
|
324
365
|
*/
|
|
325
366
|
function* causeAndWait(env: Env, ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Steps {
|
|
367
|
+
const clause = clauseAt(env, ctx, kind, place);
|
|
368
|
+
if (clause !== undefined) return "ok" in clause ? clause : yield* clause.answer(payload);
|
|
326
369
|
if (!env.running) return cause(env, ctx, kind, payload, place);
|
|
327
370
|
const effect = recordEffect(env, ctx, kind, payload, place);
|
|
328
371
|
if ("ok" in effect) return effect;
|
|
@@ -480,6 +523,17 @@ function evalInput(env: Env, seen: Seen, at: At): EvalInput {
|
|
|
480
523
|
};
|
|
481
524
|
}
|
|
482
525
|
|
|
526
|
+
/** This function evaluates a part under a frame: the part also gets the kinds of the clauses (CAP.GRANT). */
|
|
527
|
+
function* handledAt(env: Env, part: At, clauses: readonly Clause[]): Steps {
|
|
528
|
+
const frame: RefFrame = { clauses, outer: refFrame(part.ctx), owner: env.effects, ended: false };
|
|
529
|
+
const ctx = { ...part.ctx, caps: [...part.ctx.caps, ...clauses.map((clause) => clause.op)], [REF_FRAME]: frame };
|
|
530
|
+
try {
|
|
531
|
+
return yield* stepsAt(env, { ...part, ctx });
|
|
532
|
+
} finally {
|
|
533
|
+
frame.ended = true;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
483
537
|
function runInput(env: Env, seen: Seen, at: At): RunInput {
|
|
484
538
|
const { split, doc } = seen;
|
|
485
539
|
const parts: RunPart[] = split.parts.map((slot) => {
|
|
@@ -489,6 +543,7 @@ function runInput(env: Env, seen: Seen, at: At): RunInput {
|
|
|
489
543
|
path: slot.path,
|
|
490
544
|
...(name === undefined ? {} : { name }),
|
|
491
545
|
evaluate: (given?: Context) => stepsAt(env, partAt(seen, at, slot, given)),
|
|
546
|
+
handled: (clauses: readonly Clause[]) => handledAt(env, partAt(seen, at, slot, undefined), clauses),
|
|
492
547
|
};
|
|
493
548
|
});
|
|
494
549
|
const fill = (items: readonly unknown[]): unknown => (split.parts.length === 0 ? doc.value : split.fill(items));
|