@mark1russell7/coeval 0.12.0 → 0.14.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 +3 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +82 -43
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/conform.ts +3 -2
- package/src/evaluate.ts +77 -39
- package/src/reference.ts +5 -3
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.14.0",
|
|
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/conform.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
type Runtime,
|
|
24
24
|
type Steps,
|
|
25
25
|
capKeys,
|
|
26
|
+
intersectCaps,
|
|
26
27
|
makeContext,
|
|
27
28
|
permits,
|
|
28
29
|
rootContext,
|
|
@@ -371,8 +372,8 @@ class Shot implements Runtime {
|
|
|
371
372
|
/** This function answers an effect as the kernel does: it checks the capability, and it asks the mock. */
|
|
372
373
|
private answer(ctx: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result {
|
|
373
374
|
const keys = capKeys(kind);
|
|
374
|
-
// The kernel keeps
|
|
375
|
-
const caps = ctx.caps
|
|
375
|
+
// The kernel keeps the capabilities that both the context and the document permit (H-GRANT, CAP.MEET).
|
|
376
|
+
const caps = intersectCaps(ctx.caps, this.context.caps);
|
|
376
377
|
const allowed = this.machine.permits === undefined ? permits(caps, keys) : this.machine.permits(caps, keys, { ...ctx, caps });
|
|
377
378
|
if (!allowed) return fail("#CAP!", `The context has no capability for the effect ${keys.join(".")}.`, { place });
|
|
378
379
|
if (this.perform === undefined) return ok(payload);
|
package/src/evaluate.ts
CHANGED
|
@@ -68,8 +68,9 @@ export function permits(caps: readonly Cap[], kind: Cap): boolean {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* This function narrows a context for a part
|
|
72
|
-
*
|
|
71
|
+
* This function narrows a context for a part. The part keeps the meet `intersectCaps` of its list and the list of its
|
|
72
|
+
* parent (CAP.MEET). Thus an evaluator cannot give a part more effects than it has. A context whose list the parent
|
|
73
|
+
* permits stays the same object.
|
|
73
74
|
*
|
|
74
75
|
* @param parent - The context of the parent.
|
|
75
76
|
* @param next - The context that the evaluator gives the part.
|
|
@@ -77,8 +78,8 @@ export function permits(caps: readonly Cap[], kind: Cap): boolean {
|
|
|
77
78
|
*/
|
|
78
79
|
export function narrow(parent: Context, next: Context): Context {
|
|
79
80
|
if (next === parent || next.caps === parent.caps) return next;
|
|
80
|
-
const caps = next.caps
|
|
81
|
-
return caps
|
|
81
|
+
const caps = intersectCaps(next.caps, parent.caps);
|
|
82
|
+
return caps === next.caps ? next : { ...next, caps };
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
/** No capabilities. One list serves each masked part. */
|
|
@@ -140,46 +141,73 @@ export function partCaps(rules: readonly CapsRule[] | undefined, path: readonly
|
|
|
140
141
|
*/
|
|
141
142
|
const GRANT = /* @__PURE__ */ Symbol("coeval.grant");
|
|
142
143
|
|
|
143
|
-
/**
|
|
144
|
-
|
|
144
|
+
/**
|
|
145
|
+
* The key of the epoch of a grant (CAP.NARROW-KEPT). The epoch is the list of capabilities that the host gave the root
|
|
146
|
+
* of the evaluation of the grant. A spread of a context keeps its epoch, as it keeps its grant. An
|
|
147
|
+
* effect and a resolution read a context of another epoch as a context without a grant.
|
|
148
|
+
*/
|
|
149
|
+
const EPOCH = /* @__PURE__ */ Symbol("coeval.epoch");
|
|
150
|
+
|
|
151
|
+
/** A context with the grant of the kernel and the epoch of that grant. */
|
|
152
|
+
type Granted = Context & { readonly [GRANT]?: readonly Cap[]; readonly [EPOCH]?: readonly Cap[] | undefined };
|
|
145
153
|
|
|
146
154
|
/**
|
|
147
|
-
* This function gives a context
|
|
148
|
-
* own list
|
|
149
|
-
* permits, and a context without a grant keeps none. The result has the grant of its capabilities.
|
|
155
|
+
* This function gives the capabilities that a context and its grant both permit, the meet `intersectCaps` (CAP.MEET).
|
|
156
|
+
* A context whose grant is its own list gives that list after one comparison. A context without a grant gives none.
|
|
150
157
|
*/
|
|
151
|
-
function
|
|
158
|
+
function grantedCaps(ctx: Context): readonly Cap[] {
|
|
152
159
|
const grant = (ctx as Granted)[GRANT];
|
|
153
|
-
if (grant === ctx.caps) return
|
|
154
|
-
|
|
160
|
+
if (grant === ctx.caps) return grant;
|
|
161
|
+
return grant === undefined ? NO_CAPS : intersectCaps(ctx.caps, grant);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* This function gives a context whose capabilities its grant permits (CAP.NARROW-CODE, CAP.MEET). A context whose grant
|
|
166
|
+
* is its own list of capabilities stays the same object. Each other context keeps the meet of its list and its grant,
|
|
167
|
+
* and a context without a grant keeps none. The result has the grant of its capabilities.
|
|
168
|
+
*/
|
|
169
|
+
function granted(ctx: Context): Context {
|
|
170
|
+
if ((ctx as Granted)[GRANT] === ctx.caps) return ctx;
|
|
171
|
+
const caps = grantedCaps(ctx);
|
|
155
172
|
const out: Granted = { ...ctx, caps, [GRANT]: caps };
|
|
156
173
|
return out;
|
|
157
174
|
}
|
|
158
175
|
|
|
159
176
|
/**
|
|
160
|
-
* This function gives
|
|
161
|
-
* the
|
|
162
|
-
*
|
|
163
|
-
* narrowed keeps its own list. Each other path, and each context whose grant is its own list, keeps the same context.
|
|
177
|
+
* This function gives a context of another epoch the epoch of the evaluation, without capabilities (CAP.NARROW-KEPT).
|
|
178
|
+
* Thus the context reads as a context without a grant. The kernel cannot know the document of the context in this
|
|
179
|
+
* evaluation.
|
|
164
180
|
*/
|
|
165
|
-
function
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
181
|
+
function ungranted(ctx: Context, epoch: readonly Cap[]): Context {
|
|
182
|
+
const out: Granted = { ...ctx, caps: NO_CAPS, [GRANT]: NO_CAPS, [EPOCH]: epoch };
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* This function gives the context in which the runtime resolves a path of compiled code (CAP.CAPS-GRANT, CAP.MEET).
|
|
188
|
+
* The context has the meet of its list and its grant, and a context without a grant has none. Thus a read of the root
|
|
189
|
+
* `caps` and a walker of a step document get the capabilities of the document from a widened context.
|
|
190
|
+
* A context that the code narrowed keeps its own list. A context whose list does not change stays the same object.
|
|
191
|
+
* A context of another epoch has no capabilities (CAP.NARROW-KEPT).
|
|
192
|
+
*/
|
|
193
|
+
function readable(ctx: Context, epoch: readonly Cap[]): Context {
|
|
194
|
+
if ((ctx as Granted)[EPOCH] !== epoch) return ctx.caps.length === 0 ? ctx : ungranted(ctx, epoch);
|
|
195
|
+
const caps = grantedCaps(ctx);
|
|
196
|
+
if (caps === ctx.caps || (caps.length === 0 && ctx.caps.length === 0)) return ctx;
|
|
171
197
|
const out: Granted = { ...ctx, caps, [GRANT]: caps };
|
|
172
198
|
return out;
|
|
173
199
|
}
|
|
174
200
|
|
|
175
201
|
/**
|
|
176
202
|
* This function gives the context of a part of a function evaluator. It is the context that the evaluator gives,
|
|
177
|
-
* narrowed by the context of the document (CAP.NARROW), with the grant of its capabilities.
|
|
203
|
+
* narrowed by the context of the document (CAP.NARROW), with the grant of its capabilities. The grant has the epoch
|
|
204
|
+
* of the document, because the document permits each of its capabilities (CAP.NARROW-KEPT).
|
|
178
205
|
*/
|
|
179
206
|
function handed(parent: Context, next: Context): Context {
|
|
180
207
|
const ctx = narrow(parent, next);
|
|
181
|
-
|
|
182
|
-
|
|
208
|
+
const epoch = (parent as Granted)[EPOCH];
|
|
209
|
+
if ((ctx as Granted)[GRANT] === ctx.caps && (ctx as Granted)[EPOCH] === epoch) return ctx;
|
|
210
|
+
const out: Granted = { ...ctx, [GRANT]: ctx.caps, [EPOCH]: epoch };
|
|
183
211
|
return out;
|
|
184
212
|
}
|
|
185
213
|
|
|
@@ -889,6 +917,11 @@ class Session implements Runtime {
|
|
|
889
917
|
|
|
890
918
|
constructor(
|
|
891
919
|
readonly machine: Machine,
|
|
920
|
+
/**
|
|
921
|
+
* The epoch of the evaluation: the list of capabilities that the host gave the root (CAP.NARROW-KEPT). A context
|
|
922
|
+
* keeps its grant only in an evaluation of the same epoch.
|
|
923
|
+
*/
|
|
924
|
+
readonly epoch: readonly Cap[],
|
|
892
925
|
readonly running: boolean,
|
|
893
926
|
readonly traced: boolean,
|
|
894
927
|
readonly perform: Perform | undefined,
|
|
@@ -912,18 +945,21 @@ class Session implements Runtime {
|
|
|
912
945
|
return this.reading !== undefined;
|
|
913
946
|
}
|
|
914
947
|
|
|
915
|
-
/**
|
|
948
|
+
/**
|
|
949
|
+
* This function resolves a path in the context that the grant permits (CAP.CAPS-GRANT, CAP.MEET). Thus the root
|
|
950
|
+
* `caps` and a walker of a step document get the capabilities of the document.
|
|
951
|
+
*/
|
|
916
952
|
resolve(ctx: Context, path: readonly unknown[], options?: ResolveOptions): Resolution {
|
|
917
|
-
return this.read(readable(ctx,
|
|
953
|
+
return this.read(readable(ctx, this.epoch), path, undefined, options);
|
|
918
954
|
}
|
|
919
955
|
|
|
920
956
|
/**
|
|
921
957
|
* This function resolves a compiled path (PLAN.PATH). Without a list of reads and without a trace, the compiled
|
|
922
|
-
* path answers at once. Each other resolution records its read, as `resolve` does.
|
|
923
|
-
*
|
|
958
|
+
* path answers at once. Each other resolution records its read, as `resolve` does. The path resolves in the context
|
|
959
|
+
* that the grant permits (CAP.CAPS-GRANT, CAP.MEET).
|
|
924
960
|
*/
|
|
925
961
|
resolvePath(given: Context, compiled: CompiledPath): Resolution {
|
|
926
|
-
const ctx = readable(given,
|
|
962
|
+
const ctx = readable(given, this.epoch);
|
|
927
963
|
if (this.reading === undefined && !this.traced && compiled.nav === this.machine) return compiled.resolve(ctx);
|
|
928
964
|
return this.read(ctx, compiled.path, compiled, undefined);
|
|
929
965
|
}
|
|
@@ -985,10 +1021,11 @@ class Session implements Runtime {
|
|
|
985
1021
|
/**
|
|
986
1022
|
* This function checks the capability of an effect, and records the effect with its address (RUN.ADDRESS). The
|
|
987
1023
|
* context keeps only the capabilities of its grant, thus compiled code cannot widen the context of its own effect
|
|
988
|
-
* (CAP.NARROW-CODE).
|
|
1024
|
+
* (CAP.NARROW-CODE). A context of another epoch has no capabilities, thus code cannot keep a context for a later
|
|
1025
|
+
* evaluation with fewer capabilities (CAP.NARROW-KEPT).
|
|
989
1026
|
*/
|
|
990
1027
|
private record(given: Context, kind: Cap, payload: unknown, place: readonly Key[]): Result | Recorded {
|
|
991
|
-
const ctx = granted(given);
|
|
1028
|
+
const ctx = (given as Granted)[EPOCH] === this.epoch ? granted(given) : ungranted(given, this.epoch);
|
|
992
1029
|
const keys = typeof kind === "string" ? kind.split(".") : kind;
|
|
993
1030
|
const text = keys.join(".");
|
|
994
1031
|
if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
|
|
@@ -1911,15 +1948,16 @@ function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonl
|
|
|
1911
1948
|
/**
|
|
1912
1949
|
* This function gives the context at the root of an evaluation. It is the context of the host, with the world of
|
|
1913
1950
|
* the machine when the context has none. The host gives the capabilities, thus the root has the grant of each one
|
|
1914
|
-
* (CAP.NARROW-CODE).
|
|
1951
|
+
* (CAP.NARROW-CODE). The list of the host is also the epoch of the grant (CAP.NARROW-KEPT). An evaluation and
|
|
1952
|
+
* `verifyReads` share it.
|
|
1915
1953
|
*
|
|
1916
1954
|
* @param machine - The machine.
|
|
1917
1955
|
* @param ctx - The context of the host.
|
|
1918
1956
|
* @returns The context at the root.
|
|
1919
1957
|
*/
|
|
1920
1958
|
export function rootContext(machine: Machine, ctx: Context): Context {
|
|
1921
|
-
if (ctx.world !== null && (ctx as Granted)[GRANT] === ctx.caps) return ctx;
|
|
1922
|
-
const root: Granted = { ...ctx, world: ctx.world === null ? worldThing(machine.world) : ctx.world, [GRANT]: ctx.caps };
|
|
1959
|
+
if (ctx.world !== null && (ctx as Granted)[GRANT] === ctx.caps && (ctx as Granted)[EPOCH] === ctx.caps) return ctx;
|
|
1960
|
+
const root: Granted = { ...ctx, world: ctx.world === null ? worldThing(machine.world) : ctx.world, [GRANT]: ctx.caps, [EPOCH]: ctx.caps };
|
|
1923
1961
|
return root;
|
|
1924
1962
|
}
|
|
1925
1963
|
|
|
@@ -1990,14 +2028,14 @@ class CompiledPlan implements Plan {
|
|
|
1990
2028
|
evaluate(context: Context = makeContext(), evalOptions?: EvalOptions): Result {
|
|
1991
2029
|
const machine = this.machine;
|
|
1992
2030
|
const ctx = rootContext(machine, context);
|
|
1993
|
-
const s = new Session(machine, false, this.traced, evalOptions?.perform, undefined, evalOptions?.effects, readingOf(evalOptions?.reads, ctx));
|
|
2031
|
+
const s = new Session(machine, ctx.caps, false, this.traced, evalOptions?.perform, undefined, evalOptions?.effects, readingOf(evalOptions?.reads, ctx));
|
|
1994
2032
|
return this.code(ctx, s);
|
|
1995
2033
|
}
|
|
1996
2034
|
|
|
1997
2035
|
run(context: Context = makeContext(), runOptions: PlanRunOptions = {}): Promise<Result> {
|
|
1998
2036
|
const machine = this.machine;
|
|
1999
2037
|
const ctx = rootContext(machine, context);
|
|
2000
|
-
const s = new Session(machine, true, this.traced, runOptions.perform, runOptions.signal, undefined, readingOf(runOptions.reads, ctx));
|
|
2038
|
+
const s = new Session(machine, ctx.caps, true, this.traced, runOptions.perform, runOptions.signal, undefined, readingOf(runOptions.reads, ctx));
|
|
2001
2039
|
return driveWaiting(this.runCode()(ctx, s), runOptions.signal);
|
|
2002
2040
|
}
|
|
2003
2041
|
}
|
|
@@ -2065,7 +2103,7 @@ export function evaluate(machine: Machine, doc: unknown, context: Context = make
|
|
|
2065
2103
|
...(given.verb === undefined ? {} : { verb: given.verb }),
|
|
2066
2104
|
});
|
|
2067
2105
|
const ctx = rootContext(machine, context);
|
|
2068
|
-
const s = new Session(machine, false, true, given.perform, undefined, undefined, readingOf(given.reads, ctx));
|
|
2106
|
+
const s = new Session(machine, ctx.caps, false, true, given.perform, undefined, undefined, readingOf(given.reads, ctx));
|
|
2069
2107
|
const result = (plan as CompiledPlan).code(ctx, s);
|
|
2070
2108
|
return record(s, result);
|
|
2071
2109
|
}
|
|
@@ -2088,7 +2126,7 @@ export async function run(machine: Machine, doc: unknown, context: Context = mak
|
|
|
2088
2126
|
...(options.verb === undefined ? {} : { verb: options.verb }),
|
|
2089
2127
|
});
|
|
2090
2128
|
const ctx = rootContext(machine, context);
|
|
2091
|
-
const s = new Session(machine, true, true, options.perform, options.signal, undefined, readingOf(options.reads, ctx));
|
|
2129
|
+
const s = new Session(machine, ctx.caps, true, true, options.perform, options.signal, undefined, readingOf(options.reads, ctx));
|
|
2092
2130
|
const result = await driveWaiting((plan as CompiledPlan).runCode()(ctx, s), options.signal);
|
|
2093
2131
|
return record(s, result);
|
|
2094
2132
|
}
|
package/src/reference.ts
CHANGED
|
@@ -171,11 +171,13 @@ function capsOfPart(rules: readonly CapsRule[] | undefined, path: readonly Key[]
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
|
-
* This function gives the context of a part. A context that the evaluator gives keeps
|
|
175
|
-
* parent (CAP.NARROW). Then the rules of the kind
|
|
174
|
+
* This function gives the context of a part. A context that the evaluator gives keeps the effects that both it and
|
|
175
|
+
* the parent permit (CAP.NARROW, CAP.MEET). A list that the parent permits stays as it is. Then the rules of the kind
|
|
176
|
+
* narrow it (CAP.CONTRACT).
|
|
176
177
|
*/
|
|
177
178
|
function contextOfPart(seen: Seen, slot: PartSlot, parent: Context, given: Context | undefined): Context {
|
|
178
|
-
const base =
|
|
179
|
+
const base =
|
|
180
|
+
given === undefined || given === parent ? parent : given.caps.every((cap) => permits(parent.caps, cap)) ? given : { ...given, caps: bothPermit(parent.caps, given.caps) };
|
|
179
181
|
const caps = capsOfPart(seen.kind?.caps, slot.path, base.caps);
|
|
180
182
|
return caps === base.caps ? base : { ...base, caps };
|
|
181
183
|
}
|