@mark1russell7/coeval 0.28.0 → 0.30.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/README.md +1 -1
- package/build/index.d.ts +17 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +85 -15
- package/build/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +1 -1
- package/src/stepper.ts +90 -18
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.30.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",
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
],
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@mark1russell7/cue": "^2.1.1",
|
|
39
|
-
"@mark1russell7/rigor-bench": "^0.2.
|
|
39
|
+
"@mark1russell7/rigor-bench": "^0.2.34",
|
|
40
40
|
"@mark1russell7/rigor-statistics": "^0.1.16",
|
|
41
41
|
"@types/node": "^26.6.4",
|
|
42
|
-
"@vitest/coverage-v8": "5.0.
|
|
42
|
+
"@vitest/coverage-v8": "^5.0.3",
|
|
43
43
|
"esbuild": "^0.28.2",
|
|
44
44
|
"typescript": "^7.0.2",
|
|
45
45
|
"vitest": "^5.0.3"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@mark1russell7/collections": "^0.
|
|
51
|
+
"@mark1russell7/collections": "^0.10.0",
|
|
52
52
|
"@mark1russell7/rigor-generators": "^0.1.8"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
package/src/index.ts
CHANGED
|
@@ -294,7 +294,7 @@ export {
|
|
|
294
294
|
print,
|
|
295
295
|
syntaxFormat,
|
|
296
296
|
} from "./syntax.ts";
|
|
297
|
-
export { type Configuration, type Frame, type StepperOptions, stepper, stepperNext, stepperStart } from "./stepper.ts";
|
|
297
|
+
export { type Configuration, type Frame, type StepperOptions, decodeConfiguration, encodeConfiguration, stepper, stepperNext, stepperStart } from "./stepper.ts";
|
|
298
298
|
export { type Definitions, type ReferenceOptions, type ReferenceRunOptions, STD_DEFINITIONS, formulaDefinitions, reference, referenceRun } from "./reference.ts";
|
|
299
299
|
export { type AgreeOptions, type AgreeRunOptions, type Agreement, type AgreeTypesOptions, type TypeAgreement, agreeTypes, type Tier, type TierInput, type TierResult, agree, agreeRun, sameValue } from "./agree.ts";
|
|
300
300
|
export { type ConformOptions, type ConformProbe, type Finding, type Obligation, conforms } from "./conform.ts";
|
package/src/stepper.ts
CHANGED
|
@@ -110,7 +110,7 @@ interface Shared {
|
|
|
110
110
|
readonly stack: readonly Frame[];
|
|
111
111
|
/** The count of the active applications of functions (FORMULA.CALL-DEPTH). */
|
|
112
112
|
readonly applying: number;
|
|
113
|
-
/** `true` after a closure
|
|
113
|
+
/** `true` after a closure goes in a value. Only then does a value need a change for the host. */
|
|
114
114
|
readonly marked: boolean;
|
|
115
115
|
/** The work of the evaluation after the last call of `stepperNext` (WORK.LIMIT). A configuration without it has done no work. */
|
|
116
116
|
readonly work?: number;
|
|
@@ -121,8 +121,9 @@ interface Shared {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
* One configuration of the stepper.
|
|
125
|
-
*
|
|
124
|
+
* One configuration of the stepper. A closure and a held error in it are values that only the stepper makes
|
|
125
|
+
* (STEPPER.FORGE). Thus a configuration goes through JSON with `encodeConfiguration` and `decodeConfiguration`
|
|
126
|
+
* (STEPPER.DATA).
|
|
126
127
|
*
|
|
127
128
|
* - `evaluate`: the document `value` starts, with a new evaluation at its depth.
|
|
128
129
|
* - `resume`: a document gave `result`, and the top frame takes it.
|
|
@@ -183,8 +184,11 @@ interface Program {
|
|
|
183
184
|
ended: boolean;
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
/** The keys of the JSON form of a configuration: a closure, a held error, and a record of data with one of these keys. */
|
|
186
188
|
const CLOSURE_MARK = "coeval.closure";
|
|
187
189
|
const HELD_MARK = "coeval.held";
|
|
190
|
+
const DATA_MARK = "coeval.data";
|
|
191
|
+
const MARKS: readonly string[] = /* @__PURE__ */ Object.freeze([CLOSURE_MARK, HELD_MARK, DATA_MARK]);
|
|
188
192
|
const CLOSURE: unique symbol = /* @__PURE__ */ Symbol.for("coeval.closure") as never;
|
|
189
193
|
const MAX_ALIASES = 32;
|
|
190
194
|
const MAX_COLLAPSE = 64;
|
|
@@ -207,13 +211,25 @@ function setField(target: Record<string, unknown>, key: string, value: unknown):
|
|
|
207
211
|
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
|
|
208
212
|
}
|
|
209
213
|
|
|
214
|
+
/**
|
|
215
|
+
* A closure of the stepper as a value. Only the construct `fn` and `decodeConfiguration` make one. A document gives only
|
|
216
|
+
* data, thus it cannot make a closure with a context of its choice (STEPPER.FORGE, D199).
|
|
217
|
+
*/
|
|
218
|
+
class StepClosure {
|
|
219
|
+
readonly closure: Closure;
|
|
220
|
+
|
|
221
|
+
constructor(closure: Closure) {
|
|
222
|
+
this.closure = closure;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
210
226
|
function closureOf(value: unknown): Closure | undefined {
|
|
211
|
-
return
|
|
227
|
+
return value instanceof StepClosure ? value.closure : undefined;
|
|
212
228
|
}
|
|
213
229
|
|
|
230
|
+
/** A held error is a `Held`, as in the plan. A record of data with the old key is data (STEPPER.FORGE, D199). */
|
|
214
231
|
function heldOf(value: unknown): EvalError | undefined {
|
|
215
|
-
|
|
216
|
-
return isRecord(value) && Object.hasOwn(value, HELD_MARK) ? (value[HELD_MARK] as unknown as EvalError) : undefined;
|
|
232
|
+
return value instanceof Held ? value.error : undefined;
|
|
217
233
|
}
|
|
218
234
|
|
|
219
235
|
/** The judgment 2: K-START and K-COMPUTED. */
|
|
@@ -466,7 +482,7 @@ function popped(c: Configuration): void {
|
|
|
466
482
|
}
|
|
467
483
|
|
|
468
484
|
/** H-SEAL: the evaluation of a part, with the frame that waits for it on top of the stack. */
|
|
469
|
-
function enter(c: Configuration, frame: { at: readonly Key[] }, site: Site, slot: Slot, given?: Context
|
|
485
|
+
function enter(c: Configuration, frame: { at: readonly Key[] }, site: Site, slot: Slot, given?: Context): Evaluating {
|
|
470
486
|
frame.at = slot.path;
|
|
471
487
|
return {
|
|
472
488
|
mode: "evaluate",
|
|
@@ -477,7 +493,7 @@ function enter(c: Configuration, frame: { at: readonly Key[] }, site: Site, slot
|
|
|
477
493
|
ctx: contextOf(site, slot, given),
|
|
478
494
|
stack: c.stack,
|
|
479
495
|
applying: c.applying,
|
|
480
|
-
marked,
|
|
496
|
+
marked: c.marked,
|
|
481
497
|
};
|
|
482
498
|
}
|
|
483
499
|
|
|
@@ -615,7 +631,7 @@ function hostClosure(p: Program, value: object, closure: Closure): unknown {
|
|
|
615
631
|
return fn;
|
|
616
632
|
}
|
|
617
633
|
|
|
618
|
-
/** This function gives a value to host code: a closure is a function
|
|
634
|
+
/** This function gives a value to host code: a closure is a function. A held error is a `Held` in the stepper too. */
|
|
619
635
|
function forHost(p: Program, c: Configuration, value: unknown): unknown {
|
|
620
636
|
p.applying = c.applying;
|
|
621
637
|
return c.marked ? hostValue(p, value) : value;
|
|
@@ -626,11 +642,9 @@ function hostValue(p: Program, value: unknown): unknown {
|
|
|
626
642
|
const items = (value as readonly unknown[]).map((item) => hostValue(p, item));
|
|
627
643
|
return items.some((item, index) => item !== value[index]) ? items : value;
|
|
628
644
|
}
|
|
629
|
-
if (!isRecord(value)) return value;
|
|
630
645
|
const closure = closureOf(value);
|
|
631
|
-
if (closure !== undefined) return hostClosure(p, value, closure);
|
|
632
|
-
|
|
633
|
-
if (held !== undefined) return new Held(held);
|
|
646
|
+
if (closure !== undefined) return hostClosure(p, value as StepClosure, closure);
|
|
647
|
+
if (!isRecord(value)) return value;
|
|
634
648
|
let out: Record<string, unknown> | undefined;
|
|
635
649
|
for (const key of Object.keys(value)) {
|
|
636
650
|
const item = hostValue(p, value[key]);
|
|
@@ -811,7 +825,7 @@ function construct(p: Program, c: Evaluating, name: string, doc: Envelope, keys:
|
|
|
811
825
|
return back(c, fail("#VALUE!", "The params of fn are a list of different texts."));
|
|
812
826
|
}
|
|
813
827
|
const closure: Closure = { params: params as readonly string[], body: partNamed(slots, "body") as Slot, site, place: placeOf(c.stack) };
|
|
814
|
-
return back({ ...c, marked: true }, ok(
|
|
828
|
+
return back({ ...c, marked: true }, ok(new StepClosure(closure)));
|
|
815
829
|
}
|
|
816
830
|
case "formula.call": {
|
|
817
831
|
const frame: CallFrame = { f: "call", at: NO_KEYS, site, args: partsUnder(slots, "args") };
|
|
@@ -1034,13 +1048,12 @@ function take(p: Program, c: Resuming, frame: Frame): Configuration {
|
|
|
1034
1048
|
return resumeValues(p, c, frame);
|
|
1035
1049
|
case "let": {
|
|
1036
1050
|
const key = String((frame.slots[frame.next - 1] as Slot).path[1]);
|
|
1037
|
-
setField(frame.names, key, result.ok ? result.value :
|
|
1038
|
-
const marked = c.marked || !result.ok;
|
|
1051
|
+
setField(frame.names, key, result.ok ? result.value : new Held(result.error));
|
|
1039
1052
|
const slot = nextOf(frame);
|
|
1040
|
-
if (slot !== undefined) return enter(c, frame, frame.site, slot
|
|
1053
|
+
if (slot !== undefined) return enter(c, frame, frame.site, slot);
|
|
1041
1054
|
const tail: Frame = { f: "tail", at: NO_KEYS };
|
|
1042
1055
|
stack[stack.length - 1] = tail;
|
|
1043
|
-
return enter(c, tail, frame.site, frame.body, { ...frame.site.ctx, names: frame.names }
|
|
1056
|
+
return enter(c, tail, frame.site, frame.body, { ...frame.site.ctx, names: frame.names });
|
|
1044
1057
|
}
|
|
1045
1058
|
case "call": {
|
|
1046
1059
|
popped(c);
|
|
@@ -1169,6 +1182,65 @@ export function stepperNext(machine: Machine, configuration: Configuration, opti
|
|
|
1169
1182
|
};
|
|
1170
1183
|
}
|
|
1171
1184
|
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* This function gives the JSON form of a configuration (STEPPER.DATA). A closure becomes a record with the key
|
|
1188
|
+
* `coeval.closure`, and a held error a record with the key `coeval.held`. A record of data with one of the keys of the
|
|
1189
|
+
* form goes in a record with the key `coeval.data`. Thus `decodeConfiguration` tells data from a closure. A value that is
|
|
1190
|
+
* not plain JSON stays as it is. A shared object stays shared, and `JSON.stringify` copies it.
|
|
1191
|
+
*/
|
|
1192
|
+
export function encodeConfiguration(configuration: Configuration): Json {
|
|
1193
|
+
const done = new Map<object, unknown>();
|
|
1194
|
+
const encode = (value: unknown): unknown => {
|
|
1195
|
+
if (typeof value !== "object" || value === null) return value;
|
|
1196
|
+
const known = done.get(value);
|
|
1197
|
+
if (known !== undefined) return known;
|
|
1198
|
+
let out: unknown = value;
|
|
1199
|
+
if (value instanceof StepClosure) out = { [CLOSURE_MARK]: encode(value.closure) };
|
|
1200
|
+
else if (value instanceof Held) out = { [HELD_MARK]: encode(value.error) };
|
|
1201
|
+
else if (Array.isArray(value)) out = (value as readonly unknown[]).map(encode);
|
|
1202
|
+
else if (isRecord(value)) {
|
|
1203
|
+
const fields: Record<string, unknown> = {};
|
|
1204
|
+
for (const key of Object.keys(value)) setField(fields, key, encode(value[key]));
|
|
1205
|
+
out = Object.keys(value).some((key) => MARKS.includes(key)) ? { [DATA_MARK]: fields } : fields;
|
|
1206
|
+
}
|
|
1207
|
+
done.set(value, out);
|
|
1208
|
+
return out;
|
|
1209
|
+
};
|
|
1210
|
+
return encode(configuration) as Json;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* This function gives the configuration of a JSON form from `encodeConfiguration` (STEPPER.DATA). Only a record with the
|
|
1215
|
+
* one key `coeval.closure` or `coeval.held` becomes a closure or a held error. The record in a record with the one key
|
|
1216
|
+
* `coeval.data` is data, also with these keys. The host gives the form, thus the stepper trusts it as it trusts a context.
|
|
1217
|
+
*/
|
|
1218
|
+
export function decodeConfiguration(data: Json): Configuration {
|
|
1219
|
+
const done = new Map<object, unknown>();
|
|
1220
|
+
const decode = (value: unknown, data: boolean): unknown => {
|
|
1221
|
+
if (typeof value !== "object" || value === null) return value;
|
|
1222
|
+
const known = data ? undefined : done.get(value);
|
|
1223
|
+
if (known !== undefined) return known;
|
|
1224
|
+
let out: unknown = value;
|
|
1225
|
+
if (Array.isArray(value)) out = (value as readonly unknown[]).map((item) => decode(item, false));
|
|
1226
|
+
else if (isRecord(value)) {
|
|
1227
|
+
const keys = Object.keys(value);
|
|
1228
|
+
const mark = !data && keys.length === 1 ? keys[0] : undefined;
|
|
1229
|
+
if (mark === CLOSURE_MARK) out = new StepClosure(decode(value[mark], false) as Closure);
|
|
1230
|
+
else if (mark === HELD_MARK) out = new Held(decode(value[mark], false) as EvalError);
|
|
1231
|
+
else if (mark === DATA_MARK && isRecord(value[mark])) out = decode(value[mark], true);
|
|
1232
|
+
else {
|
|
1233
|
+
const fields: Record<string, unknown> = {};
|
|
1234
|
+
for (const key of keys) setField(fields, key, decode(value[key], false));
|
|
1235
|
+
out = fields;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
if (!data) done.set(value, out);
|
|
1239
|
+
return out;
|
|
1240
|
+
};
|
|
1241
|
+
return decode(data, false) as Configuration;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1172
1244
|
/**
|
|
1173
1245
|
* This function evaluates a document with an explicit stack (K95, D158). It gives the result of `reference` for each
|
|
1174
1246
|
* document of the kernel and the formula kit (AGREE.STEPPER). The nesting of a document does not use the stack of the
|