@mark1russell7/coeval 0.2.1 → 0.3.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 +2 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +44 -11
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/deref.ts +20 -5
- package/src/evaluate.ts +17 -5
- package/src/formula.ts +3 -2
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.3.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/deref.ts
CHANGED
|
@@ -259,14 +259,21 @@ function mayBeRef(thing: unknown): boolean {
|
|
|
259
259
|
return typeof thing === "object" && thing !== null && (thing as { readonly type?: unknown }).type === "ref";
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
/** This function tells if each step of a path from an index is a key. */
|
|
263
|
+
function keysFrom(path: readonly unknown[], start: number): boolean {
|
|
264
|
+
for (let index = start; index < path.length; index += 1) if (!isKey(path[index])) return false;
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
262
268
|
/**
|
|
263
|
-
* This function resolves the common path without a record of hops: a root, then keys
|
|
264
|
-
*
|
|
265
|
-
* follow, a step document and a
|
|
269
|
+
* This function resolves the common path without a record of hops: a root, then keys. Plain values answer the
|
|
270
|
+
* keys, or a host thing answers all of them in one call. It gives `undefined` when the path needs the full
|
|
271
|
+
* algorithm. Examples are a host thing without `resolve`, an envelope, a reference to follow, a step document and a
|
|
272
|
+
* failure of a step (for its exact reason).
|
|
266
273
|
*/
|
|
267
274
|
function resolveQuick(nav: Navigator, ctx: Context, path: readonly unknown[]): Resolution | undefined {
|
|
268
275
|
const root = path[0];
|
|
269
|
-
if (root !== "names" && root !== "self" && root !== "event") return undefined;
|
|
276
|
+
if (root !== "names" && root !== "self" && root !== "event" && root !== "world") return undefined;
|
|
270
277
|
if (nav.roots !== undefined && !(nav.roots as readonly string[]).includes(root)) return undefined;
|
|
271
278
|
if (nav.adopt !== undefined) return undefined;
|
|
272
279
|
let here: unknown = ctx[root];
|
|
@@ -274,7 +281,15 @@ function resolveQuick(nav: Navigator, ctx: Context, path: readonly unknown[]): R
|
|
|
274
281
|
for (let index = 1; index < path.length; index += 1) {
|
|
275
282
|
if (typeof here !== "object" || here === null) return undefined;
|
|
276
283
|
if (!plain && mayBeRef(here)) return undefined;
|
|
277
|
-
if ((here as Partial<HostThing>)[THING] === true)
|
|
284
|
+
if ((here as Partial<HostThing>)[THING] === true) {
|
|
285
|
+
const thing = here as HostThing;
|
|
286
|
+
if (thing.resolve === undefined || !keysFrom(path, index)) return undefined;
|
|
287
|
+
const keys = path.slice(index) as readonly Key[];
|
|
288
|
+
const reached = thing.resolve(keys);
|
|
289
|
+
if (reached === undefined) return { ok: false, code: "#N/A", reason: `The path ${keys.join(".")} reaches nothing.`, hops: NO_HOPS };
|
|
290
|
+
if (!plain && mayBeRef(reached.thing)) return undefined;
|
|
291
|
+
return { ok: true, thing: reached.thing, hops: NO_HOPS };
|
|
292
|
+
}
|
|
278
293
|
const step = path[index];
|
|
279
294
|
if (Array.isArray(here)) {
|
|
280
295
|
if (typeof step !== "number" || !Number.isInteger(step) || step < 0 || step >= here.length) return undefined;
|
package/src/evaluate.ts
CHANGED
|
@@ -445,9 +445,14 @@ export interface EvalOptions {
|
|
|
445
445
|
readonly effects?: Effect[];
|
|
446
446
|
}
|
|
447
447
|
|
|
448
|
+
/** The options of a resolution without a record of hops, and with one. One object of each serves each call. */
|
|
449
|
+
const WITHOUT_HOPS: ResolveOptions = Object.freeze({ hops: false });
|
|
450
|
+
const WITH_HOPS: ResolveOptions = Object.freeze({ hops: true });
|
|
451
|
+
|
|
448
452
|
/** This class holds the state of one evaluation or run: the machine, the effects, the clock and the trace. */
|
|
449
453
|
class Session implements Runtime {
|
|
450
|
-
|
|
454
|
+
/** The list of the effects. It is made at the first effect, thus an evaluation without effects makes no list. */
|
|
455
|
+
private list: Effect[] | undefined;
|
|
451
456
|
tick = 0;
|
|
452
457
|
nextId = 0;
|
|
453
458
|
current: Building | undefined;
|
|
@@ -461,13 +466,19 @@ class Session implements Runtime {
|
|
|
461
466
|
readonly signal: StopSignal | undefined,
|
|
462
467
|
effects: Effect[] | undefined,
|
|
463
468
|
) {
|
|
464
|
-
this.
|
|
469
|
+
this.list = effects;
|
|
465
470
|
this.holder = traced ? holderNode() : undefined;
|
|
466
471
|
this.current = this.holder;
|
|
467
472
|
}
|
|
468
473
|
|
|
474
|
+
/** The effects of the evaluation, in their order. */
|
|
475
|
+
get effects(): Effect[] {
|
|
476
|
+
return (this.list ??= []);
|
|
477
|
+
}
|
|
478
|
+
|
|
469
479
|
resolve(ctx: Context, path: readonly unknown[], options?: ResolveOptions): Resolution {
|
|
470
|
-
|
|
480
|
+
const hops = options?.hops ?? this.traced;
|
|
481
|
+
return resolve(this.machine, ctx, path, hops ? WITH_HOPS : WITHOUT_HOPS);
|
|
471
482
|
}
|
|
472
483
|
|
|
473
484
|
private permitted(ctx: Context, keys: readonly Key[]): boolean {
|
|
@@ -479,8 +490,9 @@ class Session implements Runtime {
|
|
|
479
490
|
const text = keys.join(".");
|
|
480
491
|
if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
|
|
481
492
|
const node = this.current;
|
|
482
|
-
const
|
|
483
|
-
this.
|
|
493
|
+
const list = this.effects;
|
|
494
|
+
const effect: Effect = { index: list.length, kind: text, path: keys, payload, node: node?.id ?? -1, at: this.tick, place, context: ctx };
|
|
495
|
+
list.push(effect);
|
|
484
496
|
if (node !== undefined) node.effects.push(effect.index);
|
|
485
497
|
return effect;
|
|
486
498
|
}
|
package/src/formula.ts
CHANGED
|
@@ -24,7 +24,8 @@ export type ExtFn = (data: Json) => Result;
|
|
|
24
24
|
* - `ext`: the extensions of the namespace `ext`, by their name. The kit has `ext.undefined` and `ext.number`.
|
|
25
25
|
* - `truthy`: the truth of a value for `if`, `and` and `or`. `undefined` is an error `#VALUE!`. The default
|
|
26
26
|
* accepts only `true` and `false`.
|
|
27
|
-
* - `otherwise`: the value of an `if` without `else`. The default is `null`.
|
|
27
|
+
* - `otherwise`: the value of an `if` without `else`. The default is `null`. A profile with this key gives its value,
|
|
28
|
+
* also `undefined`.
|
|
28
29
|
* - `logic`: what `and` and `or` give: a truth value (the default), or the value that decided.
|
|
29
30
|
* - `omit`: the errors that leave a field out of a `record`, as an absent value.
|
|
30
31
|
* - `missing`: the values that a reference cannot give. Such a value gives `#N/A`.
|
|
@@ -150,7 +151,7 @@ export function formulaKinds(): Readonly<Record<string, WorldEntry>> {
|
|
|
150
151
|
*/
|
|
151
152
|
export function formulaEvaluators(profile: FormulaProfile = {}): Readonly<Record<string, Evaluator>> {
|
|
152
153
|
const truthy = profile.truthy ?? strictTruthy;
|
|
153
|
-
const otherwise = ok(profile.otherwise
|
|
154
|
+
const otherwise = ok(Object.hasOwn(profile, "otherwise") ? profile.otherwise : null);
|
|
154
155
|
const logicValue = profile.logic === "value";
|
|
155
156
|
const omit = profile.omit;
|
|
156
157
|
const missing = profile.missing;
|