@mark1russell7/coeval 0.1.0 → 0.2.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.js CHANGED
@@ -7,13 +7,16 @@ const ENVELOPE_KEYS = [
7
7
  "own"
8
8
  ];
9
9
  /**
10
- * This function tells if a value is a JSON record (not an array and not null).
10
+ * This function tells if a value is a plain record: an object with the prototype of a plain object, or with no
11
+ * prototype. A list, `null`, a date, a map and an instance of a class are not plain records.
11
12
  *
12
13
  * @param value - The value to examine.
13
- * @returns `true` for a record.
14
+ * @returns `true` for a plain record.
14
15
  */
15
16
  function isRecord(value) {
16
- return typeof value === "object" && value !== null && !Array.isArray(value);
17
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
18
+ const proto = Object.getPrototypeOf(value);
19
+ return proto === Object.prototype || proto === null;
17
20
  }
18
21
  /**
19
22
  * This function reads a field of a record. It reads only the own fields, thus `constructor` is not a field.
@@ -69,6 +72,13 @@ function typePath(type) {
69
72
  function typeName(type) {
70
73
  return typePath(type)?.join(".") ?? "(computed)";
71
74
  }
75
+ function headerFits(value) {
76
+ if (!isTypeRef(field(value, "type")) || !Object.hasOwn(value, "value")) return false;
77
+ const name = field(value, "name");
78
+ if (name !== void 0 && typeof name !== "string") return false;
79
+ const own = field(value, "own");
80
+ return own === void 0 || isRecord(own);
81
+ }
72
82
  /**
73
83
  * This function tells if a value has the form of an envelope. That form is a record with a reference to a type
74
84
  * and a `value`. It has no other keys than the keys of an envelope. A contract decides where an envelope is
@@ -78,13 +88,32 @@ function typeName(type) {
78
88
  * @returns `true` when the value has the form of an envelope.
79
89
  */
80
90
  function isEnvelope(value) {
81
- if (!isRecord(value)) return false;
82
- if (!isTypeRef(field(value, "type")) || !Object.hasOwn(value, "value")) return false;
83
- const name = field(value, "name");
84
- if (name !== void 0 && typeof name !== "string") return false;
85
- const own = field(value, "own");
86
- if (own !== void 0 && !isRecord(own)) return false;
87
- return Object.keys(value).every((key) => ENVELOPE_KEYS.includes(key));
91
+ if (!isRecord(value) || !headerFits(value)) return false;
92
+ for (const key in value) if (Object.hasOwn(value, key) && !ENVELOPE_KEYS.includes(key)) return false;
93
+ return true;
94
+ }
95
+ /**
96
+ * This function reads a stored document: an envelope that also has the keys of a store, for example `{ _id, type,
97
+ * value }`. Only the keys in the list can be beside the keys of the envelope.
98
+ *
99
+ * @param value - The value to examine.
100
+ * @param header - The keys of the store that the document can have.
101
+ * @returns The envelope and the keys of the store, or `undefined` when the value has another form.
102
+ */
103
+ function stored(value, header) {
104
+ if (!isRecord(value) || !headerFits(value)) return void 0;
105
+ const keys = {};
106
+ const own = {};
107
+ for (const key in value) {
108
+ if (!Object.hasOwn(value, key)) continue;
109
+ if (ENVELOPE_KEYS.includes(key)) own[key] = value[key];
110
+ else if (header.includes(key)) keys[key] = value[key];
111
+ else return void 0;
112
+ }
113
+ return {
114
+ envelope: fromJson(own),
115
+ header: keys
116
+ };
88
117
  }
89
118
  /**
90
119
  * This function makes an envelope.
@@ -144,6 +173,149 @@ function fromJson(value) {
144
173
  ...isRecord(own) ? { own: ownEntries } : {}
145
174
  });
146
175
  }
176
+ /**
177
+ * This function tells if two JSON values are equal. Equal values have the same texts, numbers and truth values,
178
+ * the same items in the same order, and the same fields. The order of the fields does not matter. It does not use the stack for the
179
+ * depth, thus a deep document cannot overflow it.
180
+ *
181
+ * @param a - The first value.
182
+ * @param b - The second value.
183
+ * @returns `true` when the values are equal.
184
+ */
185
+ function docEquals(a, b) {
186
+ const stack = [[a, b]];
187
+ while (stack.length > 0) {
188
+ const [x, y] = stack.pop();
189
+ if (Object.is(x, y)) continue;
190
+ if (typeof x !== "object" || typeof y !== "object" || x === null || y === null) return false;
191
+ if (Array.isArray(x) !== Array.isArray(y)) return false;
192
+ if (Array.isArray(x)) {
193
+ const ys = y;
194
+ if (x.length !== ys.length) return false;
195
+ for (let index = 0; index < x.length; index += 1) stack.push([x[index], ys[index]]);
196
+ continue;
197
+ }
198
+ const xr = x;
199
+ const yr = y;
200
+ const keys = Object.keys(xr);
201
+ if (keys.length !== Object.keys(yr).length) return false;
202
+ for (const key of keys) {
203
+ if (!Object.hasOwn(yr, key)) return false;
204
+ stack.push([xr[key], yr[key]]);
205
+ }
206
+ }
207
+ return true;
208
+ }
209
+ //#endregion
210
+ //#region src/errors.ts
211
+ /**
212
+ * This function makes a result with a value.
213
+ *
214
+ * @param value - The value.
215
+ * @returns The result.
216
+ */
217
+ function ok(value) {
218
+ return {
219
+ ok: true,
220
+ value
221
+ };
222
+ }
223
+ /**
224
+ * This function makes a result with an error value.
225
+ *
226
+ * @param code - The code of the error.
227
+ * @param reason - One sentence that tells the cause.
228
+ * @param more - The place and the cause of the error, when they are known.
229
+ * @returns The result.
230
+ */
231
+ function fail(code, reason, more = {}) {
232
+ return {
233
+ ok: false,
234
+ error: {
235
+ code,
236
+ reason,
237
+ ...more.place === void 0 ? {} : { place: more.place },
238
+ ...more.cause === void 0 ? {} : { cause: more.cause }
239
+ }
240
+ };
241
+ }
242
+ /**
243
+ * This function gives a failed result its place, when it has none. A result with a value, or an error with a
244
+ * place, stays the same object.
245
+ *
246
+ * @param result - The result.
247
+ * @param place - The keys from the root document to the document that failed.
248
+ * @returns The result with a place.
249
+ */
250
+ function placed(result, place) {
251
+ if (result.ok || result.error.place !== void 0) return result;
252
+ return {
253
+ ok: false,
254
+ error: {
255
+ ...result.error,
256
+ place
257
+ }
258
+ };
259
+ }
260
+ /**
261
+ * An exception that carries an error value through code of the host. A closure of a formula throws it when the
262
+ * host calls the closure and the closure fails. The kernel catches it and gives its error value again.
263
+ */
264
+ var Failure = class extends Error {
265
+ error;
266
+ /**
267
+ * This constructor makes the exception.
268
+ *
269
+ * @param error - The error value that the exception carries.
270
+ */
271
+ constructor(error) {
272
+ super(`${error.code} ${error.reason}`);
273
+ this.name = "Failure";
274
+ this.error = error;
275
+ }
276
+ };
277
+ /**
278
+ * This function tells if an exception is the overflow of the stack of the host.
279
+ *
280
+ * @param error - The exception.
281
+ * @returns `true` for a stack overflow.
282
+ */
283
+ function isStackOverflow(error) {
284
+ return error instanceof RangeError && /call stack/i.test(error.message);
285
+ }
286
+ /**
287
+ * This function changes an exception into an error value. A `Failure` gives its error value. A stack overflow
288
+ * gives `#DEPTH!`. Each other exception gives `#CALC!`, with the exception as the cause.
289
+ *
290
+ * @param error - The exception.
291
+ * @param who - A short text that names the code that threw, for example `the evaluator math.add`.
292
+ * @returns The failed result.
293
+ */
294
+ function thrown(error, who) {
295
+ if (error instanceof Failure) return {
296
+ ok: false,
297
+ error: error.error
298
+ };
299
+ if (isStackOverflow(error)) return fail("#DEPTH!", `The nesting of ${who} is too deep for the stack of the host.`, { cause: error });
300
+ return fail("#CALC!", `${upper(who)} threw: ${describe(error)}`, { cause: error });
301
+ }
302
+ function upper(text) {
303
+ return text.length === 0 ? text : `${text[0]?.toUpperCase() ?? ""}${text.slice(1)}`;
304
+ }
305
+ /**
306
+ * This function gives a short text for an exception or any value. It does not throw.
307
+ *
308
+ * @param value - The value.
309
+ * @returns The text.
310
+ */
311
+ function describe(value) {
312
+ try {
313
+ if (value instanceof Error) return `${value.name}: ${value.message}`;
314
+ return String(value);
315
+ } catch {
316
+ return "(a value without a text)";
317
+ }
318
+ }
147
319
  //#endregion
148
320
  //#region src/world.ts
149
321
  /**
@@ -164,13 +336,23 @@ function isKind(entry) {
164
336
  function isAlias(entry) {
165
337
  return entry !== void 0 && entry.type === "ref" && Array.isArray(entry.value);
166
338
  }
339
+ /**
340
+ * This function tells if an entry of the world is a namespace.
341
+ *
342
+ * @param entry - The entry.
343
+ * @returns `true` for a namespace.
344
+ */
345
+ function isNamespace(entry) {
346
+ return entry !== void 0 && !isKind(entry) && !isAlias(entry) && Object.hasOwn(entry, "kinds");
347
+ }
167
348
  function entryAt(entries, key) {
168
349
  const name = String(key);
169
350
  return Object.hasOwn(entries, name) ? entries[name] : void 0;
170
351
  }
171
352
  /**
172
353
  * This function finds the kind that a type names. It walks the path from the root of the world, one key at a
173
- * time. An alias on the way is followed until a kind answers (collapse). A cycle of aliases stops the lookup.
354
+ * time. An alias on the way is followed until a kind answers (collapse). A cycle of aliases stops the lookup. In a
355
+ * namespace with a fallback, a key that the namespace does not have gets the fallback kind.
174
356
  *
175
357
  * @param world - The world.
176
358
  * @param type - The reference to the type.
@@ -187,16 +369,25 @@ function lookupKind(world, type) {
187
369
  let aliases = 0;
188
370
  for (;;) {
189
371
  let entries = world.kinds;
372
+ let fallback;
190
373
  let entry;
191
374
  let followed = false;
192
375
  for (let index = 0; index < path.length; index += 1) {
193
376
  const key = path[index];
194
377
  entry = entryAt(entries, key);
195
- if (entry === void 0) return {
196
- ok: false,
197
- reason: "unknown",
198
- path
199
- };
378
+ if (entry === void 0) {
379
+ if (fallback !== void 0) return {
380
+ ok: true,
381
+ kind: fallback,
382
+ path,
383
+ aliases
384
+ };
385
+ return {
386
+ ok: false,
387
+ reason: "unknown",
388
+ path
389
+ };
390
+ }
200
391
  if (isAlias(entry)) {
201
392
  aliases += 1;
202
393
  if (aliases > 32) return {
@@ -215,12 +406,13 @@ function lookupKind(world, type) {
215
406
  break;
216
407
  }
217
408
  if (index < path.length - 1) {
218
- if (isKind(entry)) return {
409
+ if (!isNamespace(entry)) return {
219
410
  ok: false,
220
411
  reason: "not-a-kind",
221
412
  path
222
413
  };
223
414
  entries = entry.kinds;
415
+ fallback = entry.fallback;
224
416
  }
225
417
  }
226
418
  if (followed) continue;
@@ -254,6 +446,14 @@ function entriesAsJson(entries) {
254
446
  else out[key] = entriesAsJson(entry.kinds);
255
447
  return out;
256
448
  }
449
+ function describeShape(shape) {
450
+ if (shape === void 0 || shape.shape === "none") return "none";
451
+ if (shape.shape === "term") return `a part${shape.of === void 0 ? "" : ` of ${shape.of}`}`;
452
+ if (shape.shape === "deep") return "each document in the value";
453
+ if (shape.shape === "list") return `a list${shape.of === void 0 ? "" : ` of ${shape.of}`}`;
454
+ if (shape.shape === "map") return `a record of parts${shape.of === void 0 ? "" : ` of ${shape.of}`}`;
455
+ return `the fields ${(Array.isArray(shape.fields) ? shape.fields : Object.keys(shape.fields)).join(", ")}`;
456
+ }
257
457
  /**
258
458
  * This function makes the document of a kind, so that a view can draw it as each other document.
259
459
  *
@@ -262,18 +462,26 @@ function entriesAsJson(entries) {
262
462
  * @returns The document of type `kind`.
263
463
  */
264
464
  function kindDocument(name, kind) {
265
- const parts = kind.parts === void 0 || kind.parts.shape === "none" ? "none" : kind.parts.shape === "list" ? `a list${kind.parts.of === void 0 ? "" : ` of ${kind.parts.of}`}` : `the fields ${kind.parts.fields.join(", ")}`;
266
465
  return {
267
466
  type: "kind",
268
467
  value: {
269
468
  name,
270
469
  verbs: { ...kind.verbs },
271
- parts,
470
+ parts: describeShape(kind.parts),
272
471
  side: kind.side ?? "derive",
273
472
  summary: kind.summary ?? ""
274
473
  }
275
474
  };
276
475
  }
476
+ /**
477
+ * This function tells if a JSON value is the record of a kind.
478
+ *
479
+ * @param value - The value.
480
+ * @returns `true` when the value has verbs.
481
+ */
482
+ function looksLikeKind(value) {
483
+ return isRecord(value) && isRecord(field(value, "verbs"));
484
+ }
277
485
  //#endregion
278
486
  //#region src/ladder.ts
279
487
  /** The rungs of the ladder, in the order that a question visits them. */
@@ -411,8 +619,11 @@ function ask(world, doc, verb, options = {}) {
411
619
  }
412
620
  //#endregion
413
621
  //#region src/deref.ts
414
- /** The brand of a host thing: an object of the host that answers the steps of a path itself. */
415
- const THING = Symbol("coeval.thing");
622
+ /**
623
+ * The brand of a host thing: an object of the host that answers the steps of a path itself. The brand is a
624
+ * global symbol, thus two copies of the kernel in one program know the things of each other.
625
+ */
626
+ const THING = Symbol.for("coeval.thing");
416
627
  /**
417
628
  * This function tells if a thing is a host thing.
418
629
  *
@@ -420,7 +631,7 @@ const THING = Symbol("coeval.thing");
420
631
  * @returns `true` for a host thing.
421
632
  */
422
633
  function isHostThing(thing) {
423
- return typeof thing === "object" && thing !== null && !Array.isArray(thing) && thing[THING] === true;
634
+ return typeof thing === "object" && thing !== null && thing[THING] === true;
424
635
  }
425
636
  /** The roots of a context. Each path starts with one of them. */
426
637
  const ROOTS = [
@@ -433,22 +644,36 @@ const ROOTS = [
433
644
  function entryOf(verbs, verb) {
434
645
  return verbs !== void 0 && Object.hasOwn(verbs, verb) ? verbs[verb] : void 0;
435
646
  }
647
+ /** The own fields of a value: the items and the length of a list, and the own properties of an object. */
436
648
  function plainStep(thing, key) {
437
649
  if (Array.isArray(thing)) {
650
+ if (key === "length") return {
651
+ found: true,
652
+ value: thing.length
653
+ };
438
654
  const index = typeof key === "number" ? key : /^\d+$/.test(key) ? Number(key) : -1;
439
- return index >= 0 ? thing[index] : void 0;
655
+ return index >= 0 && index < thing.length ? {
656
+ found: true,
657
+ value: thing[index]
658
+ } : void 0;
659
+ }
660
+ if (typeof thing === "object" && thing !== null) {
661
+ const name = String(key);
662
+ return Object.hasOwn(thing, name) ? {
663
+ found: true,
664
+ value: thing[name]
665
+ } : void 0;
440
666
  }
441
- if (isRecord(thing) && typeof key === "string") return field(thing, key);
442
667
  }
443
668
  /**
444
669
  * This function answers one step of a path on a thing, through the ladder of the verb `deref`. A host thing
445
- * answers for itself (own). A document asks its own entry, then its kind. The default goes into the value: a field
446
- * of a record, or an item of a list.
670
+ * answers for itself (own). A document asks its own entry, then its kind. The default goes into the value: an own
671
+ * field of an object, or an item of a list.
447
672
  *
448
673
  * @param nav - The world and the code of the evaluators.
449
674
  * @param thing - The thing that the path reached.
450
675
  * @param key - The key of the step.
451
- * @returns What the step reaches and the rung that answered, or `undefined`.
676
+ * @returns What the step reaches and the rung that answered, or `undefined` when it reaches nothing.
452
677
  */
453
678
  function derefStep(nav, thing, key) {
454
679
  if (isHostThing(thing)) {
@@ -458,9 +683,10 @@ function derefStep(nav, thing, key) {
458
683
  by: "own"
459
684
  };
460
685
  }
461
- const envelope = fromJson(thing);
462
- if (envelope !== void 0) {
463
- const own = entryOf(envelope.own, "deref");
686
+ if (nav.plain !== true && isEnvelope(thing)) {
687
+ const envelope = thing;
688
+ const ownEntries = field(envelope, "own");
689
+ const own = isRecord(ownEntries) ? entryOf(ownEntries, "deref") : void 0;
464
690
  const ownCode = own === void 0 ? void 0 : nav.derefs?.[own];
465
691
  if (ownCode !== void 0) {
466
692
  const reached = ownCode(envelope, key);
@@ -481,38 +707,43 @@ function derefStep(nav, thing, key) {
481
707
  }
482
708
  const reached = plainStep(envelope.value, key);
483
709
  return reached === void 0 ? void 0 : {
484
- thing: reached,
710
+ thing: reached.value,
485
711
  by: "default"
486
712
  };
487
713
  }
488
714
  const reached = plainStep(thing, key);
489
715
  return reached === void 0 ? void 0 : {
490
- thing: reached,
716
+ thing: reached.value,
491
717
  by: "default"
492
718
  };
493
719
  }
494
720
  /**
495
- * This function tells if a thing is a reference document: an envelope of the type `ref`.
721
+ * This function tells if a thing is a reference document: an envelope of the type `ref` with a list for its path.
496
722
  *
497
723
  * @param thing - The thing.
498
724
  * @returns `true` for a reference document.
499
725
  */
500
726
  function isRef(thing) {
501
- if (thing === void 0 || isHostThing(thing)) return false;
502
- const envelope = fromJson(thing);
503
- if (envelope === void 0 || !Array.isArray(envelope.value)) return false;
504
- const path = typePath(envelope.type);
727
+ if (!isEnvelope(thing) || !Array.isArray(thing.value)) return false;
728
+ const path = typePath(thing.type);
505
729
  return path !== void 0 && path.length === 1 && path[0] === "ref";
506
730
  }
731
+ const worlds = /* @__PURE__ */ new WeakMap();
507
732
  /**
508
733
  * This function makes the thing of the root `world`: the entries of the world as JSON. An alias is a reference
509
- * document in it, thus a walk through the world follows each alias (collapse).
734
+ * document in it, thus a walk through the world follows each alias (collapse). The thing of a world is made one
735
+ * time.
510
736
  *
511
737
  * @param world - The world.
512
738
  * @returns The thing.
513
739
  */
514
740
  function worldThing(world) {
515
- return entriesAsJson(world.kinds);
741
+ let thing = worlds.get(world);
742
+ if (thing === void 0) {
743
+ thing = entriesAsJson(world.kinds);
744
+ worlds.set(world, thing);
745
+ }
746
+ return thing;
516
747
  }
517
748
  /**
518
749
  * This function makes a context. The world comes from the navigator when the call gives none.
@@ -530,37 +761,96 @@ function context(parts = {}) {
530
761
  };
531
762
  }
532
763
  const MAX_COLLAPSE = 64;
764
+ const NO_HOPS = Object.freeze([]);
765
+ /** A value that can be a reference document: a record with the type `ref`. The full check comes after. */
766
+ function mayBeRef(thing) {
767
+ return typeof thing === "object" && thing !== null && thing.type === "ref";
768
+ }
769
+ /**
770
+ * This function resolves the common path without a record of hops: a root, then keys, through plain values. It
771
+ * gives `undefined` when the path needs the full algorithm. Examples are a host thing, an envelope, a reference to
772
+ * follow, a step document and a failure (for its exact reason).
773
+ */
774
+ function resolveQuick(nav, ctx, path) {
775
+ const root = path[0];
776
+ if (root !== "names" && root !== "self" && root !== "event") return void 0;
777
+ if (nav.roots !== void 0 && !nav.roots.includes(root)) return void 0;
778
+ if (nav.adopt !== void 0) return void 0;
779
+ let here = ctx[root];
780
+ const plain = nav.plain === true;
781
+ for (let index = 1; index < path.length; index += 1) {
782
+ if (typeof here !== "object" || here === null) return void 0;
783
+ if (!plain && mayBeRef(here)) return void 0;
784
+ if (here[THING] === true) return void 0;
785
+ const step = path[index];
786
+ if (Array.isArray(here)) {
787
+ if (typeof step !== "number" || !Number.isInteger(step) || step < 0 || step >= here.length) return void 0;
788
+ here = here[step];
789
+ continue;
790
+ }
791
+ if (typeof step !== "string" || !Object.hasOwn(here, step)) return void 0;
792
+ if (!plain && isEnvelope(here)) return void 0;
793
+ here = here[step];
794
+ }
795
+ if (!plain && mayBeRef(here)) return void 0;
796
+ return {
797
+ ok: true,
798
+ thing: here,
799
+ hops: NO_HOPS
800
+ };
801
+ }
533
802
  /**
534
803
  * This function resolves a path from a context, and records each hop.
535
804
  *
536
805
  * The first step is a root of the context. Each later step is a key or a step document. A key goes up the ladder
537
806
  * of the verb `deref` on the thing that the path reached. A step document is evaluated by the walker of its kind,
538
807
  * with the thing that the path reached. After each hop, a reference document is followed until the path reaches
539
- * something else (collapse).
808
+ * something else (collapse), except in a plain navigation.
809
+ *
810
+ * The codes of a failure:
811
+ *
812
+ * - `#REF!`: the path has no root, or a root that is not open here.
813
+ * - `#NAME?`: the root `names` has no such name.
814
+ * - `#N/A`: a step reaches nothing.
815
+ * - `#STEP?`: a step is not a key and not a step document.
816
+ * - `#CYCLE!`: the references form a cycle.
540
817
  *
541
818
  * @param nav - The world and the code of the evaluators.
542
819
  * @param ctx - The context.
543
820
  * @param path - The steps.
821
+ * @param options - `hops: false` does not record the hops.
544
822
  * @returns What the path reached and each hop, or an error.
545
823
  */
546
- function resolve(nav, ctx, path) {
547
- const hops = [];
824
+ function resolve(nav, ctx, path, options = {}) {
825
+ if (options.hops === false) {
826
+ const quick = resolveQuick(nav, ctx, path);
827
+ if (quick !== void 0) return quick;
828
+ }
829
+ const hops = options.hops === false ? void 0 : [];
830
+ const done = hops ?? NO_HOPS;
831
+ const failed = (code, reason) => ({
832
+ ok: false,
833
+ code,
834
+ reason,
835
+ hops: done
836
+ });
548
837
  let collapses = 0;
838
+ const adopt = nav.adopt ?? ((value) => value);
549
839
  const collapse = (thing) => {
550
840
  let here = thing;
841
+ if (nav.plain === true) return {
842
+ ok: true,
843
+ thing: here,
844
+ hops: done
845
+ };
551
846
  while (isRef(here)) {
552
847
  collapses += 1;
553
- if (collapses > MAX_COLLAPSE) return {
554
- ok: false,
555
- code: "#CYCLE!",
556
- reason: "The references form a cycle.",
557
- hops
558
- };
848
+ if (collapses > MAX_COLLAPSE) return failed("#CYCLE!", "The references form a cycle.");
559
849
  const inner = walk(here.value);
560
850
  if (!inner.ok) return inner;
561
- here = inner.thing;
562
- hops.push({
563
- step: here === null ? null : "collapse",
851
+ here = adopt(inner.thing);
852
+ hops?.push({
853
+ step: "collapse",
564
854
  thing: here,
565
855
  by: "collapse"
566
856
  });
@@ -568,19 +858,15 @@ function resolve(nav, ctx, path) {
568
858
  return {
569
859
  ok: true,
570
860
  thing: here,
571
- hops
861
+ hops: done
572
862
  };
573
863
  };
574
864
  const walk = (steps) => {
575
- const [root, ...rest] = steps;
576
- if (typeof root !== "string" || !ROOTS.includes(root)) return {
577
- ok: false,
578
- code: "#REF!",
579
- reason: `A path starts with a root: ${ROOTS.join(", ")}.`,
580
- hops
581
- };
582
- let here = ctx[root];
583
- hops.push({
865
+ const root = steps[0];
866
+ if (typeof root !== "string" || !ROOTS.includes(root)) return failed("#REF!", `A path starts with a root: ${ROOTS.join(", ")}.`);
867
+ if (nav.roots !== void 0 && !nav.roots.includes(root)) return failed("#REF!", `The root ${root} is not open here. The open roots are ${nav.roots.join(", ")}.`);
868
+ let here = adopt(ctx[root]);
869
+ hops?.push({
584
870
  step: root,
585
871
  thing: here,
586
872
  by: "root"
@@ -588,50 +874,54 @@ function resolve(nav, ctx, path) {
588
874
  const first = collapse(here);
589
875
  if (!first.ok) return first;
590
876
  here = first.thing;
591
- for (const step of rest) {
877
+ for (let index = 1; index < steps.length; index += 1) {
878
+ const step = steps[index];
879
+ if (isHostThing(here) && here.resolve !== void 0 && steps.slice(index).every(isKey)) {
880
+ const keys = steps.slice(index);
881
+ const reached = here.resolve(keys);
882
+ if (reached === void 0) return failed("#N/A", `The path ${keys.join(".")} reaches nothing.`);
883
+ const thing = adopt(reached.thing);
884
+ hops?.push({
885
+ step: keys,
886
+ thing,
887
+ by: "own"
888
+ });
889
+ return collapse(thing);
890
+ }
592
891
  if (isKey(step)) {
593
892
  const reached = derefStep(nav, here, step);
594
- if (reached === void 0) return {
595
- ok: false,
596
- code: "#REF!",
597
- reason: `The step ${String(step)} reaches nothing.`,
598
- hops
599
- };
600
- hops.push({
893
+ if (reached === void 0) {
894
+ if (root === "names" && index === 1) return failed("#NAME?", `The context has no name ${String(step)}.`);
895
+ return failed("#N/A", `The step ${String(step)} reaches nothing.`);
896
+ }
897
+ here = adopt(reached.thing);
898
+ hops?.push({
601
899
  step,
602
- thing: reached.thing,
900
+ thing: here,
603
901
  by: reached.by
604
902
  });
605
- here = reached.thing;
606
903
  } else {
607
- const envelope = fromJson(step);
904
+ const envelope = isEnvelope(step) ? step : void 0;
608
905
  const found = envelope === void 0 ? void 0 : lookupKind(nav.world, envelope.type);
609
906
  const name = found?.ok === true ? entryOf(found.kind.verbs, "step") : void 0;
610
907
  const walker = name === void 0 ? void 0 : nav.walkers?.[name];
611
- if (envelope === void 0 || walker === void 0) return {
612
- ok: false,
613
- code: "#STEP?",
614
- reason: "A step is a key, or a document of a kind with the verb step.",
615
- hops
616
- };
908
+ if (envelope === void 0 || walker === void 0) return failed("#STEP?", "A step is a key, or a document of a kind with the verb step.");
617
909
  const result = walker({
618
910
  data: envelope.value,
619
911
  here,
620
912
  context: ctx,
621
- deref: (thing, key) => derefStep(nav, thing, key)?.thing
913
+ deref: (thing, key) => {
914
+ const reached = derefStep(nav, thing, key);
915
+ return reached === void 0 ? void 0 : adopt(reached.thing);
916
+ }
622
917
  });
623
- if (!result.ok) return {
624
- ok: false,
625
- code: "#REF!",
626
- reason: result.reason,
627
- hops
628
- };
629
- hops.push({
630
- step,
631
- thing: result.thing,
918
+ if (!result.ok) return failed("#N/A", result.reason);
919
+ here = adopt(result.thing);
920
+ hops?.push({
921
+ step: envelope,
922
+ thing: here,
632
923
  by: "step"
633
924
  });
634
- here = result.thing;
635
925
  }
636
926
  const next = collapse(here);
637
927
  if (!next.ok) return next;
@@ -640,19 +930,14 @@ function resolve(nav, ctx, path) {
640
930
  return {
641
931
  ok: true,
642
932
  thing: here,
643
- hops
933
+ hops: done
644
934
  };
645
935
  };
646
- if (path.length === 0) return {
647
- ok: false,
648
- code: "#REF!",
649
- reason: "A path has at least one step: its root.",
650
- hops
651
- };
936
+ if (path.length === 0) return failed("#REF!", "A path has at least one step: its root.");
652
937
  return walk(path);
653
938
  }
654
939
  /**
655
- * This function gives the value of a thing: the thing itself for JSON, or the value that a host thing gives.
940
+ * This function gives the value of a thing: the value that a host thing gives, or else the thing itself.
656
941
  *
657
942
  * @param thing - The thing.
658
943
  * @returns The value.
@@ -701,89 +986,173 @@ function tag(value, kind) {
701
986
  }
702
987
  //#endregion
703
988
  //#region src/parts.ts
989
+ var Bad = class {
990
+ reason;
991
+ at;
992
+ constructor(reason, at) {
993
+ this.reason = reason;
994
+ this.at = at;
995
+ }
996
+ };
997
+ function setOwn$1(target, key, value) {
998
+ Object.defineProperty(target, key, {
999
+ value,
1000
+ enumerable: true,
1001
+ writable: true,
1002
+ configurable: true
1003
+ });
1004
+ }
1005
+ function hasSlots(template) {
1006
+ return template.t !== "data";
1007
+ }
704
1008
  /**
705
- * This function splits a value into its data and its parts, by the contract of its kind. The evaluator and the
706
- * view use the same split, thus a picture shows the parts that the evaluation gets.
1009
+ * This function splits a value into its parts, by the shape that its kind declares. The evaluator, the view and
1010
+ * the static kit use the same split, thus each one sees the same parts.
707
1011
  *
708
- * @param spec - The contract of the parts.
1012
+ * @param shape - The shape of the value. The default is data.
709
1013
  * @param value - The value of the document.
710
- * @returns The data and the parts, or the reason why the value does not obey the contract.
1014
+ * @returns The parts and a way to fill their places, or the reason why the value does not have the shape.
711
1015
  */
712
- function splitParts(spec, value) {
713
- const of = (slot, declared) => declared === void 0 ? slot : {
714
- ...slot,
715
- of: declared
1016
+ function splitParts(shape, value) {
1017
+ const parts = [];
1018
+ const slot = (item, path, of) => {
1019
+ parts.push({
1020
+ path,
1021
+ key: path.join("."),
1022
+ item,
1023
+ ...of === void 0 ? {} : { of }
1024
+ });
1025
+ return {
1026
+ t: "slot",
1027
+ index: parts.length - 1
1028
+ };
716
1029
  };
717
- if (spec === void 0 || spec.shape === "none") return {
718
- ok: true,
719
- data: value,
720
- parts: []
1030
+ const deep = (item, path, of) => {
1031
+ if (isEnvelope(item)) return slot(item, path, of);
1032
+ if (Array.isArray(item)) {
1033
+ const items = item.map((child, index) => deep(child, [...path, index], of));
1034
+ return items.some(hasSlots) ? {
1035
+ t: "list",
1036
+ items
1037
+ } : {
1038
+ t: "data",
1039
+ value: item
1040
+ };
1041
+ }
1042
+ if (isRecord(item)) {
1043
+ const fields = Object.keys(item).map((key) => [key, deep(item[key], [...path, key], of)]);
1044
+ return fields.some(([, child]) => hasSlots(child)) ? {
1045
+ t: "record",
1046
+ fields
1047
+ } : {
1048
+ t: "data",
1049
+ value: item
1050
+ };
1051
+ }
1052
+ return {
1053
+ t: "data",
1054
+ value: item
1055
+ };
721
1056
  };
722
- if (spec.shape === "list") {
723
- if (!Array.isArray(value)) return {
724
- ok: false,
725
- reason: "The value must be a list."
1057
+ const walk = (spec, item, path) => {
1058
+ if (spec === void 0 || spec.shape === "none") return {
1059
+ t: "data",
1060
+ value: item
726
1061
  };
1062
+ if (spec.shape === "term") return slot(item, path, spec.of);
1063
+ if (spec.shape === "deep") return deep(item, path, spec.of);
1064
+ if (spec.shape === "list") {
1065
+ if (!Array.isArray(item)) throw new Bad("The value must be a list.", path);
1066
+ const each = spec.item ?? {
1067
+ shape: "term",
1068
+ ...spec.of === void 0 ? {} : { of: spec.of }
1069
+ };
1070
+ return {
1071
+ t: "list",
1072
+ items: item.map((child, index) => walk(each, child, [...path, index]))
1073
+ };
1074
+ }
1075
+ if (!isRecord(item)) throw new Bad("The value must be a record.", path);
1076
+ if (spec.shape === "map") {
1077
+ const each = spec.item ?? {
1078
+ shape: "term",
1079
+ ...spec.of === void 0 ? {} : { of: spec.of }
1080
+ };
1081
+ return {
1082
+ t: "record",
1083
+ fields: Object.keys(item).map((key) => [key, walk(each, item[key], [...path, key])])
1084
+ };
1085
+ }
1086
+ for (const name of spec.required ?? []) if (!Object.hasOwn(item, name)) throw new Bad(`The field ${name} is missing.`, path);
1087
+ const named = Array.isArray(spec.fields) ? Object.fromEntries(spec.fields.map((name) => [name, {
1088
+ shape: "term",
1089
+ ...spec.of === void 0 ? {} : { of: spec.of }
1090
+ }])) : spec.fields;
1091
+ const fields = [];
1092
+ for (const key of Object.keys(item)) {
1093
+ const fieldShape = Object.hasOwn(named, key) ? named[key] : spec.rest;
1094
+ fields.push([key, walk(fieldShape, item[key], [...path, key])]);
1095
+ }
727
1096
  return {
728
- ok: true,
729
- data: null,
730
- parts: value.map((item, index) => of({
731
- key: String(index),
732
- item
733
- }, spec.of))
1097
+ t: "record",
1098
+ fields
734
1099
  };
735
- }
736
- if (!isRecord(value)) return {
737
- ok: false,
738
- reason: "The value must be a record."
739
1100
  };
740
- const record = value;
741
- const rest = {};
742
- for (const [key, item] of Object.entries(record)) if (!spec.fields.includes(key)) rest[key] = item;
743
- const parts = [];
744
- for (const key of spec.fields) {
745
- const item = field(record, key);
746
- if (item !== void 0) parts.push(of({
747
- key,
748
- item
749
- }, spec.of));
1101
+ let template;
1102
+ try {
1103
+ template = walk(shape, value, []);
1104
+ } catch (error) {
1105
+ if (error instanceof Bad) return {
1106
+ ok: false,
1107
+ reason: error.reason,
1108
+ at: error.at
1109
+ };
1110
+ throw error;
750
1111
  }
751
- return {
752
- ok: true,
753
- data: rest,
754
- parts
1112
+ const strip = (node) => {
1113
+ if (node.t === "data") return node.value;
1114
+ if (node.t === "slot") return void 0;
1115
+ if (node.t === "list") return node.items.every((child) => child.t === "slot") ? null : node.items.map((child) => strip(child) ?? null);
1116
+ const out = {};
1117
+ for (const [key, child] of node.fields) {
1118
+ const kept = strip(child);
1119
+ if (kept !== void 0) setOwn$1(out, key, kept);
1120
+ }
1121
+ return out;
1122
+ };
1123
+ const fillWith = (node, handles) => {
1124
+ if (node.t === "data") return node.value;
1125
+ if (node.t === "slot") return handles[node.index];
1126
+ if (node.t === "list") return node.items.map((child) => fillWith(child, handles));
1127
+ const out = {};
1128
+ for (const [key, child] of node.fields) setOwn$1(out, key, fillWith(child, handles));
1129
+ return out;
755
1130
  };
756
- }
757
- //#endregion
758
- //#region src/evaluate.ts
759
- /**
760
- * This function makes a result with a value.
761
- *
762
- * @param value - The value.
763
- * @returns The result.
764
- */
765
- function ok(value) {
766
1131
  return {
767
1132
  ok: true,
768
- value
1133
+ parts,
1134
+ data: strip(template) ?? null,
1135
+ fill: (handles) => fillWith(template, handles)
769
1136
  };
770
1137
  }
771
1138
  /**
772
- * This function makes a result with an error value.
1139
+ * This function tells if the path of a part matches a pattern of a scope rule. The key `"*"` of a pattern matches
1140
+ * each key.
773
1141
  *
774
- * @param code - The code of the error.
775
- * @param reason - One sentence that tells the cause.
776
- * @returns The result.
1142
+ * @param path - The path of the part.
1143
+ * @param pattern - The pattern.
1144
+ * @returns `true` when the path matches.
777
1145
  */
778
- function fail(code, reason) {
779
- return {
780
- ok: false,
781
- error: {
782
- code,
783
- reason
784
- }
785
- };
1146
+ function pathMatches(path, pattern) {
1147
+ if (path.length !== pattern.length) return false;
1148
+ for (let index = 0; index < path.length; index += 1) {
1149
+ const want = pattern[index];
1150
+ if (want !== "*" && String(want) !== String(path[index])) return false;
1151
+ }
1152
+ return true;
786
1153
  }
1154
+ //#endregion
1155
+ //#region src/evaluate.ts
787
1156
  /**
788
1157
  * This function makes a context with names and capabilities. The other roots are empty, and an evaluation sets
789
1158
  * the world.
@@ -801,194 +1170,557 @@ function makeContext(names = {}, caps = [], self = null) {
801
1170
  });
802
1171
  }
803
1172
  /**
1173
+ * This function gives the keys of a capability or of a kind of effect: a text is split at its dots.
1174
+ *
1175
+ * @param kind - The text or the keys.
1176
+ * @returns The keys, as texts.
1177
+ */
1178
+ function capKeys(kind) {
1179
+ return typeof kind === "string" ? kind.split(".") : kind.map(String);
1180
+ }
1181
+ /**
804
1182
  * This function tells if the capabilities permit an effect. A capability permits the kind of its name, and each kind
805
- * in its namespace: `store` permits `store.update`.
1183
+ * under it: `store` permits `store.update`. The comparison is key by key, thus `fs.read` does not permit the
1184
+ * kind with the keys `fs` and `read.json`.
806
1185
  *
807
1186
  * @param caps - The capabilities of the context.
808
- * @param kind - The kind of the effect.
1187
+ * @param kind - The kind of the effect: a text with dots, or its keys.
809
1188
  * @returns `true` when one capability permits it.
810
1189
  */
811
1190
  function permits(caps, kind) {
812
- return caps.some((cap) => cap === kind || kind.startsWith(`${cap}.`));
1191
+ const want = capKeys(kind);
1192
+ return caps.some((cap) => {
1193
+ const have = capKeys(cap);
1194
+ return have.length <= want.length && have.every((key, index) => key === want[index]);
1195
+ });
1196
+ }
1197
+ /**
1198
+ * This function narrows a context for a part: the part keeps only the capabilities that the context of its parent
1199
+ * permits. Thus an evaluator cannot give a part more effects than it has.
1200
+ *
1201
+ * @param parent - The context of the parent.
1202
+ * @param next - The context that the evaluator gives the part.
1203
+ * @returns The context of the part.
1204
+ */
1205
+ function narrow(parent, next) {
1206
+ if (next === parent || next.caps === parent.caps) return next;
1207
+ const caps = next.caps.filter((cap) => permits(parent.caps, cap));
1208
+ return caps.length === next.caps.length ? next : {
1209
+ ...next,
1210
+ caps
1211
+ };
813
1212
  }
814
1213
  function selfLabel(self) {
815
- if (isHostThing(self)) return self.label;
816
- return self === null ? "none" : JSON.stringify(self).slice(0, 40);
1214
+ try {
1215
+ if (isHostThing(self)) return self.label;
1216
+ if (self === null || self === void 0) return "none";
1217
+ return (JSON.stringify(self) ?? describe(self)).slice(0, 40);
1218
+ } catch {
1219
+ return describe(self).slice(0, 40);
1220
+ }
817
1221
  }
818
1222
  const NO_ANSWER = {
819
1223
  verb: "eval",
820
1224
  error: "no-answer",
821
1225
  steps: []
822
1226
  };
1227
+ /** This class holds the state of one evaluation or run: the machine, the effects, the clock and the trace. */
1228
+ var Session = class {
1229
+ machine;
1230
+ running;
1231
+ traced;
1232
+ perform;
1233
+ signal;
1234
+ effects;
1235
+ tick = 0;
1236
+ nextId = 0;
1237
+ current;
1238
+ holder;
1239
+ constructor(machine, running, traced, perform, signal, effects) {
1240
+ this.machine = machine;
1241
+ this.running = running;
1242
+ this.traced = traced;
1243
+ this.perform = perform;
1244
+ this.signal = signal;
1245
+ this.effects = effects ?? [];
1246
+ this.holder = traced ? holderNode() : void 0;
1247
+ this.current = this.holder;
1248
+ }
1249
+ resolve(ctx, path, options) {
1250
+ return resolve(this.machine, ctx, path, { hops: options?.hops ?? this.traced });
1251
+ }
1252
+ permitted(ctx, keys) {
1253
+ return this.machine.permits === void 0 ? permits(ctx.caps, keys) : this.machine.permits(ctx.caps, keys, ctx);
1254
+ }
1255
+ record(ctx, kind, payload, place) {
1256
+ const keys = typeof kind === "string" ? kind.split(".") : kind;
1257
+ const text = keys.join(".");
1258
+ if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
1259
+ const node = this.current;
1260
+ const effect = {
1261
+ index: this.effects.length,
1262
+ kind: text,
1263
+ path: keys,
1264
+ payload,
1265
+ node: node?.id ?? -1,
1266
+ at: this.tick,
1267
+ place,
1268
+ context: ctx
1269
+ };
1270
+ this.effects.push(effect);
1271
+ if (node !== void 0) node.effects.push(effect.index);
1272
+ return effect;
1273
+ }
1274
+ effect(ctx, kind, payload, place) {
1275
+ const text = typeof kind === "string" ? kind : kind.join(".");
1276
+ if (this.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
1277
+ const made = this.record(ctx, kind, payload, place);
1278
+ return "ok" in made ? made : ok(payload);
1279
+ }
1280
+ *runEffect(ctx, kind, payload, place) {
1281
+ const made = this.record(ctx, kind, payload, place);
1282
+ if ("ok" in made) return made;
1283
+ const perform = this.perform;
1284
+ if (!this.running || perform === void 0) return ok(payload);
1285
+ const failed = (error) => fail("#CALC!", `The host failed the effect ${made.kind}: ${describe(error)}`, {
1286
+ place,
1287
+ cause: error
1288
+ });
1289
+ let done;
1290
+ try {
1291
+ done = perform(made);
1292
+ } catch (error) {
1293
+ return failed(error);
1294
+ }
1295
+ if (!(done instanceof Promise)) return ok(done);
1296
+ return yield done.then((value) => ok(value), failed);
1297
+ }
1298
+ *all(list, options = {}) {
1299
+ const results = new Array(list.length);
1300
+ const limit = Math.max(1, options.concurrency ?? list.length);
1301
+ const active = [];
1302
+ let next = 0;
1303
+ const advance = (index, steps, input) => {
1304
+ let step;
1305
+ try {
1306
+ step = input.threw ? steps.throw(input.error) : steps.next(input.value);
1307
+ } catch (error) {
1308
+ results[index] = thrown(error, "a part");
1309
+ return;
1310
+ }
1311
+ if (step.done === true) {
1312
+ results[index] = step.value;
1313
+ return;
1314
+ }
1315
+ const item = {
1316
+ index,
1317
+ steps,
1318
+ pending: Promise.resolve()
1319
+ };
1320
+ item.pending = step.value.then((value) => ({
1321
+ item,
1322
+ value,
1323
+ threw: false
1324
+ }), (error) => ({
1325
+ item,
1326
+ error,
1327
+ threw: true
1328
+ }));
1329
+ active.push(item);
1330
+ };
1331
+ const start = () => {
1332
+ while (active.length < limit && next < list.length) {
1333
+ const index = next;
1334
+ next += 1;
1335
+ const begin = list[index];
1336
+ let steps;
1337
+ try {
1338
+ steps = begin();
1339
+ } catch (error) {
1340
+ results[index] = thrown(error, "a part");
1341
+ continue;
1342
+ }
1343
+ advance(index, steps, { threw: false });
1344
+ }
1345
+ };
1346
+ start();
1347
+ while (active.length > 0) {
1348
+ const settled = yield Promise.race(active.map((item) => item.pending));
1349
+ active.splice(active.indexOf(settled.item), 1);
1350
+ advance(settled.item.index, settled.item.steps, settled);
1351
+ start();
1352
+ }
1353
+ return results;
1354
+ }
1355
+ enter(meta, ctx) {
1356
+ const node = {
1357
+ id: this.nextId,
1358
+ path: meta.path,
1359
+ doc: meta.doc,
1360
+ declared: meta.declared,
1361
+ answer: meta.answer,
1362
+ side: meta.side,
1363
+ names: Object.keys(ctx.names),
1364
+ scope: ctx.names,
1365
+ caps: ctx.caps,
1366
+ self: selfLabel(ctx.self),
1367
+ enter: this.tick,
1368
+ exit: this.tick,
1369
+ result: fail("#NOEVAL", "The evaluation did not end."),
1370
+ children: [],
1371
+ effects: []
1372
+ };
1373
+ this.nextId += 1;
1374
+ this.tick += 1;
1375
+ (this.current ?? this.holder).children.push(node);
1376
+ return node;
1377
+ }
1378
+ exit(node, result) {
1379
+ node.result = result;
1380
+ node.exit = this.tick;
1381
+ this.tick += 1;
1382
+ }
1383
+ };
1384
+ function holderNode() {
1385
+ return {
1386
+ id: -1,
1387
+ path: [],
1388
+ doc: {
1389
+ type: "?",
1390
+ value: null
1391
+ },
1392
+ declared: false,
1393
+ answer: NO_ANSWER,
1394
+ side: "derive",
1395
+ names: [],
1396
+ scope: {},
1397
+ caps: [],
1398
+ self: "none",
1399
+ enter: 0,
1400
+ exit: 0,
1401
+ result: ok(null),
1402
+ children: [],
1403
+ effects: []
1404
+ };
1405
+ }
823
1406
  /** This function drives steps that cannot wait. */
824
1407
  function drive(steps) {
825
1408
  const step = steps.next();
826
1409
  if (step.done === true) return step.value;
827
1410
  step.value.catch(() => void 0);
1411
+ steps.return(fail("#CALC!", "A part waited, and its parent cannot wait."));
828
1412
  return fail("#CALC!", "A part waited, and its parent cannot wait.");
829
1413
  }
1414
+ const ABORTED = Symbol("aborted");
830
1415
  /** This function drives steps that can wait. The steps before the first wait happen at once, in the call. */
831
- async function driveWaiting(steps) {
1416
+ async function driveWaiting(steps, signal) {
1417
+ const abort = () => {
1418
+ const reason = fail("#ABORT", `The host stopped the run: ${describe(signal?.reason ?? "no reason")}`, { cause: signal?.reason });
1419
+ try {
1420
+ steps.return(reason);
1421
+ } catch {}
1422
+ return reason;
1423
+ };
1424
+ const aborted = signal === void 0 ? void 0 : new Promise((resolve) => {
1425
+ if (signal.aborted) resolve(ABORTED);
1426
+ else signal.addEventListener("abort", () => resolve(ABORTED), { once: true });
1427
+ });
1428
+ if (signal?.aborted === true) return abort();
832
1429
  let step = steps.next();
833
1430
  while (step.done !== true) {
834
1431
  let value;
835
1432
  try {
836
- value = await step.value;
1433
+ value = aborted === void 0 ? await step.value : await Promise.race([step.value, aborted]);
837
1434
  } catch (error) {
838
1435
  step = steps.throw(error);
839
1436
  continue;
840
1437
  }
1438
+ if (value === ABORTED) return abort();
841
1439
  step = steps.next(value);
842
1440
  }
843
1441
  return step.value;
844
1442
  }
845
- function* visit(s, value, path, declared, ctx, parent, canWait) {
846
- const envelope = fromJson(value);
847
- const doc = envelope ?? typed(declared ?? "?", value);
848
- const node = {
849
- id: s.nextId,
850
- path,
1443
+ function isCompiledEvaluator(evaluator) {
1444
+ return typeof evaluator === "object" && evaluator !== null && typeof evaluator.compile === "function";
1445
+ }
1446
+ function isCompiledRunner(runner) {
1447
+ return typeof runner === "object" && runner !== null && typeof runner.compileRun === "function";
1448
+ }
1449
+ function failing(c, meta, place, code, reason) {
1450
+ const result = fail(code, reason, { place });
1451
+ const plain = () => result;
1452
+ return finish(c, meta, plain, function* () {
1453
+ return result;
1454
+ });
1455
+ }
1456
+ function finish(c, meta, code, run) {
1457
+ if (!c.traced) return {
1458
+ code,
1459
+ run
1460
+ };
1461
+ const tracedCode = (ctx, rt) => {
1462
+ const s = rt;
1463
+ const node = s.enter(meta, ctx);
1464
+ const before = s.current;
1465
+ s.current = node;
1466
+ let result;
1467
+ try {
1468
+ result = code(ctx, rt);
1469
+ } finally {
1470
+ s.current = before;
1471
+ }
1472
+ s.exit(node, result);
1473
+ return result;
1474
+ };
1475
+ const tracedRun = function* (ctx, rt) {
1476
+ const s = rt;
1477
+ const node = s.enter(meta, ctx);
1478
+ const steps = run(ctx, rt);
1479
+ let input;
1480
+ let threw = false;
1481
+ for (;;) {
1482
+ const before = s.current;
1483
+ s.current = node;
1484
+ let step;
1485
+ try {
1486
+ step = threw ? steps.throw(input) : steps.next(input);
1487
+ } finally {
1488
+ s.current = before;
1489
+ }
1490
+ if (step.done === true) {
1491
+ s.exit(node, step.value);
1492
+ return step.value;
1493
+ }
1494
+ try {
1495
+ input = yield step.value;
1496
+ threw = false;
1497
+ } catch (error) {
1498
+ input = error;
1499
+ threw = true;
1500
+ }
1501
+ }
1502
+ };
1503
+ return {
1504
+ code: tracedCode,
1505
+ run: tracedRun
1506
+ };
1507
+ }
1508
+ function compileNode(c, value, place, declared, depth) {
1509
+ const envelope = isEnvelope(value) ? value : void 0;
1510
+ const doc = envelope ?? {
1511
+ type: declared ?? "?",
1512
+ value
1513
+ };
1514
+ const meta0 = {
1515
+ path: place.map(String),
851
1516
  doc,
852
1517
  declared: envelope === void 0,
853
1518
  answer: NO_ANSWER,
854
- side: "derive",
855
- names: Object.keys(ctx.names),
856
- scope: ctx.names,
857
- caps: ctx.caps,
858
- self: selfLabel(ctx.self),
859
- enter: s.tick,
860
- exit: s.tick,
861
- result: fail("#NOEVAL", "The evaluation did not end."),
862
- children: [],
863
- effects: []
1519
+ side: "derive"
864
1520
  };
865
- s.nextId += 1;
866
- s.tick += 1;
867
- parent.children.push(node);
868
- const finish = (result) => {
869
- node.result = result;
870
- node.exit = s.tick;
871
- s.tick += 1;
872
- return result;
1521
+ if (envelope === void 0 && declared === void 0) return failing(c, meta0, place, "#PART?", "The value has no envelope, and its position declares no type.");
1522
+ if (depth > c.maxDepth) return failing(c, meta0, place, "#DEPTH!", `The nesting is deeper than ${c.maxDepth} documents.`);
1523
+ const answer = ask(c.machine.world, envelope ?? {}, "eval", declared === void 0 ? {} : { declaredType: declared });
1524
+ const found = lookupKind(c.machine.world, doc.type);
1525
+ const kind = found.ok ? found.kind : void 0;
1526
+ const meta = {
1527
+ ...meta0,
1528
+ answer,
1529
+ side: kind?.side ?? "derive"
873
1530
  };
874
- if (envelope === void 0 && declared === void 0) return finish(fail("#PART?", "The value has no envelope, and its position declares no type."));
875
- const maxDepth = s.machine.maxDepth ?? 200;
876
- if (path.length > maxDepth) return finish(fail("#DEPTH!", `The nesting is deeper than ${maxDepth} documents.`));
877
- const answer = ask(s.machine.world, envelope ?? {}, "eval", declared === void 0 ? {} : { declaredType: declared });
878
- node.answer = answer;
879
- const kind = kindOf(s.machine.world, doc.type);
880
- node.side = kind?.side ?? "derive";
881
- if (answer.error === "unknown-kind") return finish(fail("#KIND?", `The world has no kind ${typeName(doc.type)}.`));
1531
+ const kindText = typeName(doc.type);
1532
+ if (answer.error === "unknown-kind") return failing(c, meta, place, "#KIND?", `The world has no kind ${kindText}.`);
882
1533
  const name = answer.evaluator;
883
- const runners = s.machine.runners ?? {};
1534
+ const runners = c.machine.runners ?? {};
884
1535
  const runner = name !== void 0 && Object.hasOwn(runners, name) ? runners[name] : void 0;
885
- const code = name !== void 0 && Object.hasOwn(s.machine.evaluators, name) ? s.machine.evaluators[name] : void 0;
886
- if (name === void 0 || runner === void 0 && code === void 0) return finish(fail("#NOEVAL", `No rung gives an evaluator for ${typeName(doc.type)}.`));
887
- if (runner !== void 0 && s.running && !canWait) return finish(fail("#CALC!", `${typeName(doc.type)} waits for effects, and its parent cannot wait. Give the parent a runner.`));
1536
+ const evaluator = name !== void 0 && Object.hasOwn(c.machine.evaluators, name) ? c.machine.evaluators[name] : void 0;
1537
+ if (name === void 0 || runner === void 0 && evaluator === void 0) return failing(c, meta, place, "#NOEVAL", `No rung gives an evaluator for ${kindText}.`);
888
1538
  const split = splitParts(kind?.parts, doc.value);
889
- if (!split.ok) return finish(fail("#VALUE!", `${split.reason} The kind is ${typeName(doc.type)}.`));
890
- const slots = split.parts.map((slot) => ({
891
- slot,
892
- header: fromJson(slot.item)
893
- }));
894
- const record = (effectKind, payload) => {
895
- if (!permits(ctx.caps, effectKind)) return fail("#CAP!", `The context has no capability for the effect ${effectKind}.`);
896
- const effect = {
897
- index: s.effects.length,
898
- kind: effectKind,
899
- payload,
900
- node: node.id,
901
- at: s.tick
1539
+ if (!split.ok) {
1540
+ const at = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
1541
+ return failing(c, meta, place, "#VALUE!", `${split.reason}${at} The kind is ${kindText}.`);
1542
+ }
1543
+ const parts = split.parts.map((slot) => {
1544
+ const compiled = compileNode(c, slot.item, [...place, ...slot.path], slot.of, depth + 1);
1545
+ const header = isEnvelope(slot.item) && isRecord(slot.item) && typeof slot.item["name"] === "string" ? slot.item["name"] : void 0;
1546
+ return {
1547
+ key: slot.key,
1548
+ path: slot.path,
1549
+ ...header === void 0 ? {} : { name: header },
1550
+ code: compiled.code,
1551
+ run: compiled.run
902
1552
  };
903
- s.effects.push(effect);
904
- node.effects.push(effect.index);
905
- return effect;
1553
+ });
1554
+ const input = {
1555
+ kind: kindText,
1556
+ typePath: found.ok ? found.path : [],
1557
+ doc,
1558
+ value: split.parts.length === 0 ? doc.value : split.fill(parts),
1559
+ data: split.data,
1560
+ parts,
1561
+ place,
1562
+ machine: c.machine
906
1563
  };
907
- const kindText = typeName(doc.type);
908
- const resolveHere = (steps) => resolve(s.machine, ctx, steps);
1564
+ const who = `the evaluator ${name}`;
909
1565
  if (runner !== void 0) {
910
- const parts = slots.map(({ slot, header }) => ({
911
- key: slot.key,
912
- ...header?.name === void 0 ? {} : { name: header.name },
913
- evaluate: (override) => visit(s, slot.item, [...path, slot.key], slot.of, override ?? ctx, node, true)
914
- }));
915
- const input = {
916
- kind: kindText,
917
- data: split.data,
918
- parts,
919
- context: ctx,
920
- resolve: resolveHere,
921
- effect: function* (effectKind, payload) {
922
- const made = record(effectKind, payload);
923
- if ("ok" in made) return made;
924
- if (!s.running || s.perform === void 0) return ok(payload);
925
- const failed = (error) => fail("#CALC!", `The host failed the effect ${effectKind}: ${String(error)}`);
926
- let done;
927
- try {
928
- done = s.perform(made);
929
- } catch (error) {
930
- return failed(error);
931
- }
932
- if (!(done instanceof Promise)) return ok(done ?? null);
933
- return yield done.then((result) => ok(result ?? null), failed);
1566
+ const inner = isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill);
1567
+ const run = function* (ctx, rt) {
1568
+ try {
1569
+ return placed(yield* inner(ctx, rt), place);
1570
+ } catch (error) {
1571
+ return placed(thrown(error, `the runner ${name}`), place);
934
1572
  }
935
1573
  };
1574
+ const code = (ctx, rt) => rt.running ? fail("#CALC!", `${kindText} waits for effects, and its parent cannot wait. Give the parent a runner.`, { place }) : drive(run(ctx, rt));
1575
+ return finish(c, meta, code, run);
1576
+ }
1577
+ const inner = isCompiledEvaluator(evaluator) ? evaluator.compile(input) : interpret(evaluator, input, split.fill);
1578
+ const code = (ctx, rt) => {
936
1579
  try {
937
- return finish(yield* runner(input));
1580
+ return placed(inner(ctx, rt), place);
938
1581
  } catch (error) {
939
- return finish(fail("#CALC!", `The runner ${name} threw: ${String(error)}`));
940
- }
941
- }
942
- const parts = slots.map(({ slot, header }) => ({
943
- key: slot.key,
944
- ...header?.name === void 0 ? {} : { name: header.name },
945
- evaluate: (override) => drive(visit(s, slot.item, [...path, slot.key], slot.of, override ?? ctx, node, false))
946
- }));
947
- const input = {
948
- kind: kindText,
949
- data: split.data,
950
- parts,
951
- context: ctx,
952
- resolve: resolveHere,
953
- effect: (effectKind, payload) => {
954
- if (s.running) return fail("#CALC!", `The effect ${effectKind} needs a runner: in a run, the host does each effect.`);
955
- const made = record(effectKind, payload);
956
- return "ok" in made ? made : ok(payload);
1582
+ return placed(thrown(error, who), place);
957
1583
  }
958
1584
  };
1585
+ return finish(c, meta, code, function* (ctx, rt) {
1586
+ return code(ctx, rt);
1587
+ });
1588
+ }
1589
+ function interpret(fn, input, fill) {
1590
+ const { kind, typePath, doc, data, place } = input;
1591
+ const compiled = input.parts;
1592
+ const hasParts = compiled.length > 0;
1593
+ return (ctx, rt) => {
1594
+ const parts = compiled.map((part) => ({
1595
+ key: part.key,
1596
+ path: part.path,
1597
+ ...part.name === void 0 ? {} : { name: part.name },
1598
+ evaluate: (override) => part.code(override === void 0 ? ctx : narrow(ctx, override), rt)
1599
+ }));
1600
+ return fn({
1601
+ kind,
1602
+ typePath,
1603
+ doc,
1604
+ value: hasParts ? fill(parts) : doc.value,
1605
+ data,
1606
+ parts,
1607
+ context: ctx,
1608
+ place,
1609
+ resolve: (path, options) => rt.resolve(ctx, path, options),
1610
+ effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place)
1611
+ });
1612
+ };
1613
+ }
1614
+ function interpretRun(fn, input, fill) {
1615
+ const { kind, typePath, doc, data, place } = input;
1616
+ const compiled = input.parts;
1617
+ const hasParts = compiled.length > 0;
1618
+ return (ctx, rt) => {
1619
+ const parts = compiled.map((part) => ({
1620
+ key: part.key,
1621
+ path: part.path,
1622
+ ...part.name === void 0 ? {} : { name: part.name },
1623
+ evaluate: (override) => part.run(override === void 0 ? ctx : narrow(ctx, override), rt)
1624
+ }));
1625
+ return fn({
1626
+ kind,
1627
+ typePath,
1628
+ doc,
1629
+ value: hasParts ? fill(parts) : doc.value,
1630
+ data,
1631
+ parts,
1632
+ context: ctx,
1633
+ place,
1634
+ resolve: (path, options) => rt.resolve(ctx, path, options),
1635
+ effect: (effectKind, payload) => rt.runEffect(ctx, effectKind, payload, place),
1636
+ all: (list, options) => rt.all(list.map((part) => () => part.evaluate()), options)
1637
+ });
1638
+ };
1639
+ }
1640
+ function rootContext(machine, ctx) {
1641
+ return ctx.world === null ? {
1642
+ ...ctx,
1643
+ world: worldThing(machine.world)
1644
+ } : ctx;
1645
+ }
1646
+ /**
1647
+ * This function compiles a document into a plan. The ladder, the kinds and the parts are known after it. A plan
1648
+ * evaluates many times. A document that does not compile gives a plan that fails with the reason, when it evaluates.
1649
+ *
1650
+ * @param machine - The world and the code of the evaluators.
1651
+ * @param doc - The document, as JSON.
1652
+ * @param options - The type of a bare root value, and a trace.
1653
+ * @returns The plan.
1654
+ */
1655
+ function prepare(machine, doc, options = {}) {
1656
+ const c = {
1657
+ machine,
1658
+ traced: options.trace === true,
1659
+ maxDepth: machine.maxDepth ?? 1e3
1660
+ };
1661
+ let root;
959
1662
  try {
960
- return finish(code(input));
1663
+ root = compileNode(c, doc, [], options.declaredType, 0);
961
1664
  } catch (error) {
962
- return finish(fail("#CALC!", `The evaluator ${name} threw: ${String(error)}`));
963
- }
964
- }
965
- function holderNode() {
966
- return {
967
- id: -1,
968
- path: [],
969
- doc: typed("?", null),
970
- declared: false,
971
- answer: NO_ANSWER,
972
- side: "derive",
973
- names: [],
974
- scope: {},
975
- caps: [],
976
- self: "none",
977
- enter: 0,
978
- exit: 0,
979
- result: ok(null),
980
- children: [],
981
- effects: []
1665
+ const result = thrown(error, "the compilation");
1666
+ root = {
1667
+ code: () => result,
1668
+ run: function* () {
1669
+ return result;
1670
+ }
1671
+ };
1672
+ }
1673
+ const compiled = {
1674
+ key: "",
1675
+ path: [],
1676
+ code: root.code,
1677
+ run: root.run
1678
+ };
1679
+ return {
1680
+ machine,
1681
+ traced: c.traced,
1682
+ root: compiled,
1683
+ evaluate(context = makeContext(), evalOptions = {}) {
1684
+ const s = new Session(machine, false, c.traced, void 0, void 0, evalOptions.effects);
1685
+ return root.code(rootContext(machine, context), s);
1686
+ },
1687
+ run(context = makeContext(), runOptions = {}) {
1688
+ const s = new Session(machine, true, c.traced, runOptions.perform, runOptions.signal, void 0);
1689
+ return driveWaiting(root.run(rootContext(machine, context), s), runOptions.signal);
1690
+ }
982
1691
  };
983
1692
  }
984
- function rootContext(machine, context) {
985
- return context.world === null ? {
986
- ...context,
987
- world: worldThing(machine.world)
988
- } : context;
1693
+ /**
1694
+ * This function makes a cache of plans for one machine. A document object compiles one time. Use it only for
1695
+ * documents that do not change after they are made.
1696
+ *
1697
+ * @param machine - The machine.
1698
+ * @param options - The options of each compilation.
1699
+ * @returns A function that gives the plan of a document.
1700
+ */
1701
+ function planner(machine, options = {}) {
1702
+ const objects = /* @__PURE__ */ new WeakMap();
1703
+ const values = /* @__PURE__ */ new Map();
1704
+ return (doc) => {
1705
+ if (typeof doc === "object" && doc !== null) {
1706
+ let plan = objects.get(doc);
1707
+ if (plan === void 0) {
1708
+ plan = prepare(machine, doc, options);
1709
+ objects.set(doc, plan);
1710
+ }
1711
+ return plan;
1712
+ }
1713
+ let plan = values.get(doc);
1714
+ if (plan === void 0) {
1715
+ plan = prepare(machine, doc, options);
1716
+ if (values.size > 1e3) values.clear();
1717
+ values.set(doc, plan);
1718
+ }
1719
+ return plan;
1720
+ };
989
1721
  }
990
- function record(s, holder, result) {
991
- const root = holder.children[0];
1722
+ function record(s, result) {
1723
+ const root = s.holder?.children[0];
992
1724
  if (root === void 0) throw new Error("The evaluation made no trace.");
993
1725
  return {
994
1726
  result,
@@ -1011,39 +1743,31 @@ function record(s, holder, result) {
1011
1743
  * @returns The result, the trace and the effects in their order.
1012
1744
  */
1013
1745
  function evaluate(machine, doc, context = makeContext(), declaredType) {
1014
- const s = {
1015
- machine,
1016
- running: false,
1017
- perform: void 0,
1018
- effects: [],
1019
- tick: 0,
1020
- nextId: 0
1021
- };
1022
- const holder = holderNode();
1023
- return record(s, holder, drive(visit(s, doc, [], declaredType, rootContext(machine, context), holder, false)));
1746
+ const plan = prepare(machine, doc, {
1747
+ trace: true,
1748
+ ...declaredType === void 0 ? {} : { declaredType }
1749
+ });
1750
+ const s = new Session(machine, false, true, void 0, void 0, void 0);
1751
+ return record(s, plan.root.code(rootContext(machine, context), s));
1024
1752
  }
1025
1753
  /**
1026
1754
  * This function runs a document: an evaluation in which the host does each effect. A runner waits for its
1027
1755
  * parts and for its effects, thus a step can read the result of the step before. The part of the run before the
1028
- * first wait happens at once, in the call.
1756
+ * first wait happens at once, in the call. The run records a trace.
1029
1757
  *
1030
1758
  * @param machine - The world, the evaluators and the runners.
1031
1759
  * @param doc - The document, as JSON.
1032
1760
  * @param context - The names and the capabilities at the root.
1033
- * @param options - The host code that does the effects, and the type of a bare root value.
1761
+ * @param options - The host code that does the effects, the type of a bare root value, and a signal.
1034
1762
  * @returns The result, the trace and the effects in their order, when the run ends.
1035
1763
  */
1036
1764
  async function run(machine, doc, context = makeContext(), options = {}) {
1037
- const s = {
1038
- machine,
1039
- running: true,
1040
- perform: options.perform,
1041
- effects: [],
1042
- tick: 0,
1043
- nextId: 0
1044
- };
1045
- const holder = holderNode();
1046
- return record(s, holder, await driveWaiting(visit(s, doc, [], options.declaredType, rootContext(machine, context), holder, true)));
1765
+ const plan = prepare(machine, doc, {
1766
+ trace: true,
1767
+ ...options.declaredType === void 0 ? {} : { declaredType: options.declaredType }
1768
+ });
1769
+ const s = new Session(machine, true, true, options.perform, options.signal, void 0);
1770
+ return record(s, await driveWaiting(plan.root.run(rootContext(machine, context), s), options.signal));
1047
1771
  }
1048
1772
  /**
1049
1773
  * This function finds a node of a trace by its path.
@@ -1053,10 +1777,16 @@ async function run(machine, doc, context = makeContext(), options = {}) {
1053
1777
  * @returns The first evaluation at that path, or `undefined` when the document did not evaluate.
1054
1778
  */
1055
1779
  function nodeAt(trace, path) {
1780
+ const want = path.map(String);
1056
1781
  let node = trace;
1057
- for (const key of path) {
1058
- node = node?.children.find((child) => child.path[child.path.length - 1] === key);
1059
- if (node === void 0) return void 0;
1782
+ let at = 0;
1783
+ while (node !== void 0 && at < want.length) {
1784
+ const here = node;
1785
+ node = here.children.find((child) => {
1786
+ const tail = child.path.slice(here.path.length);
1787
+ return tail.length > 0 && tail.every((key, index) => key === want[at + index]);
1788
+ });
1789
+ if (node !== void 0) at += node.path.length - here.path.length;
1060
1790
  }
1061
1791
  return node;
1062
1792
  }
@@ -1068,13 +1798,24 @@ function nodeAt(trace, path) {
1068
1798
  */
1069
1799
  function flatten(trace) {
1070
1800
  const out = [];
1071
- const walk = (node) => {
1801
+ const stack = [trace];
1802
+ while (stack.length > 0) {
1803
+ const node = stack.pop();
1072
1804
  out.push(node);
1073
- node.children.forEach(walk);
1074
- };
1075
- walk(trace);
1805
+ for (let index = node.children.length - 1; index >= 0; index -= 1) stack.push(node.children[index]);
1806
+ }
1076
1807
  return out.sort((a, b) => a.enter - b.enter);
1077
1808
  }
1809
+ /**
1810
+ * This function lists the errors of a trace: each node whose result is an error and whose parts did not fail
1811
+ * the same way. These nodes are where each error started.
1812
+ *
1813
+ * @param trace - The root of the trace.
1814
+ * @returns The errors, in the order of their start.
1815
+ */
1816
+ function failures(trace) {
1817
+ return flatten(trace).filter((node) => !node.result.ok && !node.children.some((child) => !child.result.ok && child.result.error === node.result.error)).map((node) => node.result.error);
1818
+ }
1078
1819
  //#endregion
1079
1820
  //#region src/stdlib.ts
1080
1821
  /**
@@ -1222,6 +1963,15 @@ const STD_WORLD = {
1222
1963
  view: "view.seq"
1223
1964
  },
1224
1965
  parts: { shape: "list" },
1966
+ scope: [{
1967
+ names: { headers: true },
1968
+ in: [["*"]],
1969
+ sequential: true
1970
+ }, {
1971
+ names: { fixed: ["last"] },
1972
+ in: [["*"]],
1973
+ sequential: true
1974
+ }],
1225
1975
  summary: "Its steps, in order. The result of a named step is a name for the later steps."
1226
1976
  },
1227
1977
  pure: {
@@ -1306,11 +2056,8 @@ const STD_EVALUATORS = {
1306
2056
  },
1307
2057
  "ref.resolve": ({ data, resolve }) => {
1308
2058
  if (!Array.isArray(data) || data.length === 0) return fail("#VALUE!", "The value of ref is a path: a list that starts with a root, for example [\"names\", \"a\"].");
1309
- const path = data;
1310
- const found = resolve(path);
1311
- if (found.ok) return ok(valueOfThing(found.thing));
1312
- if (path[0] === "names" && found.hops.length === 1) return fail("#NAME?", `The context has no name ${String(path[1])}.`);
1313
- return fail(found.code, found.reason);
2059
+ const found = resolve(data);
2060
+ return found.ok ? ok(valueOfThing(found.thing)) : fail(found.code, found.reason);
1314
2061
  },
1315
2062
  "tree.node": (input) => treeNode(input.parts, input.context, input.kind)
1316
2063
  };
@@ -1360,34 +2107,44 @@ function treeNode(parts, context, type) {
1360
2107
  }
1361
2108
  return ok(out);
1362
2109
  }
2110
+ function hostType(node, wanted) {
2111
+ return isHostThing(node) && node.type === wanted;
2112
+ }
1363
2113
  /**
1364
- * The walker of `nearest`: it follows a key (the default is `parent`) until the node that it reaches has the
1365
- * wanted type. With `self: true`, the node where the walk starts is the first candidate. A node without that key
1366
- * ends the walk with an error.
2114
+ * This function makes a walker `nearest`. The walker follows a key (the default is `parent`) until the node that it
2115
+ * reaches has the wanted type. With `self: true`, the node where the walk starts is the first candidate. A node
2116
+ * without that key ends the walk with an error.
2117
+ *
2118
+ * @param options - The test of a type and the most hops.
2119
+ * @returns The walker.
1367
2120
  */
1368
- const nearest = ({ data, here, deref }) => {
1369
- const follow = isRecord(data) && typeof field(data, "follow") === "string" ? String(field(data, "follow")) : "parent";
1370
- const wanted = isRecord(data) ? field(data, "type") : void 0;
1371
- const fromSelf = isRecord(data) && field(data, "self") === true;
1372
- let at = here;
1373
- for (let hops = 0; hops < 256; hops += 1) {
1374
- if (hops > 0 || !fromSelf) at = at === void 0 || at === null ? void 0 : deref(at, follow);
1375
- if (at === void 0 || at === null) return {
2121
+ function nearestWalker(options = {}) {
2122
+ const matches = options.matches ?? hostType;
2123
+ const maxHops = options.maxHops ?? 256;
2124
+ return ({ data, here, deref }) => {
2125
+ const follow = isRecord(data) && typeof field(data, "follow") === "string" ? String(field(data, "follow")) : "parent";
2126
+ const wanted = isRecord(data) ? field(data, "type") : void 0;
2127
+ const fromSelf = isRecord(data) && field(data, "self") === true;
2128
+ let at = here;
2129
+ for (let hops = 0; hops < maxHops; hops += 1) {
2130
+ if (hops > 0 || !fromSelf) at = at === void 0 || at === null ? void 0 : deref(at, follow);
2131
+ if (at === void 0 || at === null) return {
2132
+ ok: false,
2133
+ reason: `No node of the type ${String(wanted)} is on the way.`
2134
+ };
2135
+ if (matches(at, wanted)) return {
2136
+ ok: true,
2137
+ thing: at
2138
+ };
2139
+ }
2140
+ return {
1376
2141
  ok: false,
1377
- reason: `No node of the type ${String(wanted)} is on the way.`
1378
- };
1379
- if ((isHostThing(at) ? at.type : void 0) === wanted) return {
1380
- ok: true,
1381
- thing: at
2142
+ reason: "The walk is too long."
1382
2143
  };
1383
- }
1384
- return {
1385
- ok: false,
1386
- reason: "The walk is too long."
1387
2144
  };
1388
- };
2145
+ }
1389
2146
  /** The walkers of the standard world, by name. */
1390
- const STD_WALKERS = { "walk.nearest": nearest };
2147
+ const STD_WALKERS = { "walk.nearest": nearestWalker() };
1391
2148
  /** This function evaluates parts one after the other, and stops at the first error. */
1392
2149
  function* inOrder(parts) {
1393
2150
  const values = [];
@@ -1573,26 +2330,26 @@ function layers(steps) {
1573
2330
  //#region src/context.ts
1574
2331
  /** The parts of a context. A term can reach outside itself only through these parts. */
1575
2332
  const PARTS = [
2333
+ "self",
1576
2334
  "names",
1577
- "focus",
1578
2335
  "world",
1579
2336
  "event",
1580
2337
  "caps"
1581
2338
  ];
1582
2339
  /** The descriptions of the parts, in the order of `PARTS`. */
1583
2340
  const PART_INFO = [
2341
+ {
2342
+ part: "self",
2343
+ title: "Self",
2344
+ reaches: "A position in a structure: the node itself, its parent, its children or its siblings.",
2345
+ direction: "Any direction that a move permits: up, down or to the side."
2346
+ },
1584
2347
  {
1585
2348
  part: "names",
1586
2349
  title: "Names",
1587
2350
  reaches: "The values that a binding gives: a let, a parameter, or the result of an earlier step.",
1588
2351
  direction: "Back, to the place where the program binds the name."
1589
2352
  },
1590
- {
1591
- part: "focus",
1592
- title: "Focus",
1593
- reaches: "A position in a structure: the node itself, its parent, its children or its siblings.",
1594
- direction: "Any direction that a move permits: up, down or to the side."
1595
- },
1596
2353
  {
1597
2354
  part: "world",
1598
2355
  title: "World",
@@ -1625,15 +2382,22 @@ const CONSTRUCTS = [
1625
2382
  side: "derive",
1626
2383
  summary: "A pure formula that gives a value at one position of a structure.",
1627
2384
  example: {
1628
- type: "formula",
2385
+ type: "let",
1629
2386
  value: {
1630
- let: { gap: 8 },
2387
+ bind: { gap: 8 },
1631
2388
  body: {
1632
- op: "add",
1633
- args: [{
1634
- ref: "width",
1635
- at: "parent"
1636
- }, { var: "gap" }]
2389
+ type: ["ops", "add"],
2390
+ value: [{
2391
+ type: "ref",
2392
+ value: [
2393
+ "self",
2394
+ "parent",
2395
+ "width"
2396
+ ]
2397
+ }, {
2398
+ type: "ref",
2399
+ value: ["names", "gap"]
2400
+ }]
1637
2401
  }
1638
2402
  }
1639
2403
  },
@@ -1643,10 +2407,10 @@ const CONSTRUCTS = [
1643
2407
  gives: "extends",
1644
2408
  note: "A let binds names for its body."
1645
2409
  },
1646
- focus: {
2410
+ self: {
1647
2411
  reads: true,
1648
2412
  gives: "moves",
1649
- note: "A move reads the parent. An axis moves the focus to each target."
2413
+ note: "A move reads the parent. An axis moves self to each target."
1650
2414
  },
1651
2415
  world: NONE,
1652
2416
  event: NONE,
@@ -1665,15 +2429,19 @@ const CONSTRUCTS = [
1665
2429
  example: {
1666
2430
  type: "seq",
1667
2431
  value: [{
1668
- type: "store.find",
2432
+ type: ["store", "find"],
1669
2433
  value: { match: { board: "b1" } },
1670
2434
  name: "cards"
1671
2435
  }, {
1672
- type: "store.patch",
2436
+ type: ["store", "patch"],
1673
2437
  value: {
1674
2438
  ids: {
1675
2439
  type: "ref",
1676
- value: "cards.ids"
2440
+ value: [
2441
+ "names",
2442
+ "cards",
2443
+ "ids"
2444
+ ]
1677
2445
  },
1678
2446
  set: { done: true }
1679
2447
  }
@@ -1685,7 +2453,7 @@ const CONSTRUCTS = [
1685
2453
  gives: "extends",
1686
2454
  note: "The result of each step is a name for the later steps."
1687
2455
  },
1688
- focus: NONE,
2456
+ self: NONE,
1689
2457
  world: {
1690
2458
  reads: true,
1691
2459
  gives: "same",
@@ -1716,7 +2484,7 @@ const CONSTRUCTS = [
1716
2484
  type: "text",
1717
2485
  value: {
1718
2486
  type: "ref",
1719
- value: "title"
2487
+ value: ["names", "title"]
1720
2488
  }
1721
2489
  }]
1722
2490
  }
@@ -1727,10 +2495,10 @@ const CONSTRUCTS = [
1727
2495
  gives: "extends",
1728
2496
  note: "The data of the node becomes a name for its children."
1729
2497
  },
1730
- focus: {
2498
+ self: {
1731
2499
  reads: true,
1732
2500
  gives: "moves",
1733
- note: "Each child becomes the focus of its own part."
2501
+ note: "Each child becomes self for its own part."
1734
2502
  },
1735
2503
  world: {
1736
2504
  reads: true,
@@ -1747,11 +2515,8 @@ const CONSTRUCTS = [
1747
2515
  side: "derive",
1748
2516
  summary: "A rule that gives the box of a child from the box of its parent.",
1749
2517
  example: {
1750
- type: "rule",
1751
- value: {
1752
- use: "stack",
1753
- with: { gap: 8 }
1754
- }
2518
+ type: ["layouts", "stack"],
2519
+ value: { gap: 8 }
1755
2520
  },
1756
2521
  uses: {
1757
2522
  names: {
@@ -1759,7 +2524,7 @@ const CONSTRUCTS = [
1759
2524
  gives: "none",
1760
2525
  note: "The rule reads its parameters."
1761
2526
  },
1762
- focus: {
2527
+ self: {
1763
2528
  reads: true,
1764
2529
  gives: "none",
1765
2530
  note: "The rule reads the box of the parent and the key of the child."
@@ -1781,10 +2546,10 @@ const CONSTRUCTS = [
1781
2546
  run: {
1782
2547
  type: "seq",
1783
2548
  value: [{
1784
- type: "card.toggle",
2549
+ type: ["card", "toggle"],
1785
2550
  value: { id: {
1786
2551
  type: "ref",
1787
- value: "event.target"
2552
+ value: ["event", "target"]
1788
2553
  } }
1789
2554
  }]
1790
2555
  }
@@ -1796,7 +2561,7 @@ const CONSTRUCTS = [
1796
2561
  gives: "same",
1797
2562
  note: "The handler reads the names of its place."
1798
2563
  },
1799
- focus: {
2564
+ self: {
1800
2565
  reads: true,
1801
2566
  gives: "same",
1802
2567
  note: "The handler knows the node that got the event."
@@ -1829,7 +2594,7 @@ const CONSTRUCTS = [
1829
2594
  collection: "cards",
1830
2595
  match: { board: {
1831
2596
  type: "ref",
1832
- value: "board"
2597
+ value: ["names", "board"]
1833
2598
  } }
1834
2599
  }
1835
2600
  },
@@ -1839,7 +2604,7 @@ const CONSTRUCTS = [
1839
2604
  gives: "none",
1840
2605
  note: "The query reads its parameters."
1841
2606
  },
1842
- focus: NONE,
2607
+ self: NONE,
1843
2608
  world: {
1844
2609
  reads: true,
1845
2610
  gives: "none",
@@ -1851,6 +2616,769 @@ const CONSTRUCTS = [
1851
2616
  }
1852
2617
  ];
1853
2618
  //#endregion
1854
- export { CONSTRUCTS, ENVELOPE_KEYS, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_EVALUATORS, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, ask, checkSteps, classify, context, dataEdges, derefStep, effectEdges, entriesAsJson, evaluate, fail, field, flatten, fromJson, isAlias, isAllowedOrder, isEnvelope, isHostThing, isKey, isKind, isRecord, isRef, isTypeRef, kindDocument, kindNamed, kindOf, layers, lookupKind, makeContext, nodeAt, ok, permits, ref, resolve, run, splitParts, tag, toJson, typeName, typePath, typed, valueOfThing, worldThing };
2619
+ //#region src/static.ts
2620
+ function valueAt(value, path) {
2621
+ let here = value;
2622
+ for (const key of path) if (Array.isArray(here)) here = here[Number(key)];
2623
+ else if (isRecord(here) && Object.hasOwn(here, String(key))) here = here[String(key)];
2624
+ else return void 0;
2625
+ return here;
2626
+ }
2627
+ function headerName(item) {
2628
+ return isEnvelope(item) && typeof item.name === "string" ? item.name : void 0;
2629
+ }
2630
+ function namesOf(source, value, covered) {
2631
+ if ("fixed" in source) return source.fixed;
2632
+ if ("keysAt" in source) {
2633
+ const at = valueAt(value, source.keysAt);
2634
+ return isRecord(at) ? Object.keys(at) : [];
2635
+ }
2636
+ if ("listAt" in source) {
2637
+ const at = valueAt(value, source.listAt);
2638
+ return Array.isArray(at) ? at.filter((item) => typeof item === "string") : [];
2639
+ }
2640
+ if ("textAt" in source) {
2641
+ const at = valueAt(value, source.textAt);
2642
+ if (typeof at === "string") return [at];
2643
+ return source.otherwise === void 0 ? [] : [source.otherwise];
2644
+ }
2645
+ return covered.map((slot) => headerName(slot.item)).filter((name) => name !== void 0);
2646
+ }
2647
+ /** The names that the scope rules of a kind give each part, and the names that only later parts see. */
2648
+ function scopesOf(rules, value, slots) {
2649
+ const sees = slots.map(() => []);
2650
+ const later = slots.map(() => []);
2651
+ for (const rule of rules ?? []) {
2652
+ const covered = slots.map((slot, index) => ({
2653
+ slot,
2654
+ index
2655
+ })).filter(({ slot }) => rule.in.some((pattern) => pathMatches(slot.path, pattern)));
2656
+ if (rule.sequential !== true) {
2657
+ const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
2658
+ for (const { index } of covered) sees[index]?.push(...names);
2659
+ continue;
2660
+ }
2661
+ if ("headers" in rule.names) {
2662
+ covered.forEach(({ index }, position) => {
2663
+ const before = covered.slice(0, position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
2664
+ const after = covered.slice(position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
2665
+ sees[index]?.push(...before);
2666
+ later[index]?.push(...after);
2667
+ });
2668
+ continue;
2669
+ }
2670
+ const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
2671
+ covered.forEach(({ index }, position) => {
2672
+ if (position > 0) sees[index]?.push(...names);
2673
+ else later[index]?.push(...names);
2674
+ });
2675
+ }
2676
+ return {
2677
+ sees,
2678
+ later
2679
+ };
2680
+ }
2681
+ /**
2682
+ * This function visits each document of a document: the root and each part at each depth, by the contracts of the
2683
+ * kinds. A part of a kind that the world does not have is not visited. The walk uses no stack of the host for the
2684
+ * depth, thus a deep document cannot overflow it.
2685
+ *
2686
+ * @param world - The world of the kinds.
2687
+ * @param doc - The document, as JSON.
2688
+ * @param visit - The function that gets each document. It gives `"skip"` to not visit the parts of a document.
2689
+ * @param options - The type of a bare root value, the names of the host, and a function for the problems.
2690
+ */
2691
+ function walk(world, doc, visit, options = {}) {
2692
+ const stack = [{
2693
+ value: doc,
2694
+ place: [],
2695
+ at: [],
2696
+ declared: options.declaredType,
2697
+ depth: 0,
2698
+ names: new Set(options.names ?? []),
2699
+ later: /* @__PURE__ */ new Set()
2700
+ }];
2701
+ while (stack.length > 0) {
2702
+ const item = stack.pop();
2703
+ const envelope = isEnvelope(item.value) ? item.value : void 0;
2704
+ if (envelope === void 0 && item.declared === void 0) {
2705
+ options.problem?.({
2706
+ place: item.place,
2707
+ at: item.at,
2708
+ code: "#PART?",
2709
+ reason: "The value has no envelope, and its position declares no type."
2710
+ });
2711
+ continue;
2712
+ }
2713
+ const doc = envelope ?? {
2714
+ type: item.declared ?? "?",
2715
+ value: item.value
2716
+ };
2717
+ const found = lookupKind(world, doc.type);
2718
+ const kind = found.ok ? found.kind : void 0;
2719
+ if (visit({
2720
+ place: item.place,
2721
+ at: item.at,
2722
+ doc,
2723
+ declared: envelope === void 0,
2724
+ ...kind === void 0 ? {} : {
2725
+ kind,
2726
+ typePath: found.ok ? found.path : []
2727
+ },
2728
+ depth: item.depth,
2729
+ names: item.names,
2730
+ later: item.later
2731
+ }) === "skip" || kind === void 0) continue;
2732
+ const split = splitParts(kind.parts, doc.value);
2733
+ if (!split.ok) {
2734
+ const at = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
2735
+ options.problem?.({
2736
+ place: item.place,
2737
+ at: item.at,
2738
+ code: "#VALUE!",
2739
+ reason: `${split.reason}${at} The kind is ${typeName(doc.type)}.`
2740
+ });
2741
+ continue;
2742
+ }
2743
+ const { sees, later } = scopesOf(kind.scope, doc.value, split.parts);
2744
+ const base = envelope === void 0 ? item.at : [...item.at, "value"];
2745
+ for (let index = split.parts.length - 1; index >= 0; index -= 1) {
2746
+ const slot = split.parts[index];
2747
+ const extra = sees[index] ?? [];
2748
+ const hidden = later[index] ?? [];
2749
+ stack.push({
2750
+ value: slot.item,
2751
+ place: [...item.place, ...slot.path],
2752
+ at: [...base, ...slot.path],
2753
+ declared: slot.of,
2754
+ depth: item.depth + 1,
2755
+ names: extra.length === 0 ? item.names : /* @__PURE__ */ new Set([...item.names, ...extra]),
2756
+ later: hidden.length === 0 ? item.later : /* @__PURE__ */ new Set([...item.later, ...hidden])
2757
+ });
2758
+ }
2759
+ }
2760
+ }
2761
+ /**
2762
+ * This function finds each reference of a document: each document of the type `ref` that a walk reaches. A
2763
+ * reference to a name that a kind around it binds is not free. The others are what the document reads from
2764
+ * outside: its dependencies.
2765
+ *
2766
+ * @param world - The world of the kinds.
2767
+ * @param doc - The document, as JSON.
2768
+ * @param options - The type of a bare root value. The names of the host do not change the result.
2769
+ * @returns The references, in the order of the document.
2770
+ */
2771
+ function refsOf(world, doc, options = {}) {
2772
+ const out = [];
2773
+ const { names: _host, ...rest } = options;
2774
+ walk(world, doc, (v) => {
2775
+ if (!isRef(v.doc)) return void 0;
2776
+ const path = v.doc.value;
2777
+ const root = typeof path[0] === "string" ? path[0] : "";
2778
+ const first = path[1];
2779
+ const bound = root === "names" && isKey(first) && v.names.has(String(first));
2780
+ out.push({
2781
+ place: v.place,
2782
+ at: v.at,
2783
+ path,
2784
+ root,
2785
+ free: !bound
2786
+ });
2787
+ return "skip";
2788
+ }, rest);
2789
+ return out;
2790
+ }
2791
+ function setAt(value, path, next) {
2792
+ if (path.length === 0) return next;
2793
+ const [key, ...rest] = path;
2794
+ if (Array.isArray(value)) {
2795
+ const copy = [...value];
2796
+ copy[Number(key)] = setAt(value[Number(key)], rest, next);
2797
+ return copy;
2798
+ }
2799
+ const record = isRecord(value) ? value : {};
2800
+ const copy = {};
2801
+ for (const name of Object.keys(record)) Object.defineProperty(copy, name, {
2802
+ value: record[name],
2803
+ enumerable: true,
2804
+ writable: true,
2805
+ configurable: true
2806
+ });
2807
+ Object.defineProperty(copy, String(key), {
2808
+ value: setAt(record[String(key)], rest, next),
2809
+ enumerable: true,
2810
+ writable: true,
2811
+ configurable: true
2812
+ });
2813
+ return copy;
2814
+ }
2815
+ /**
2816
+ * This function changes the free references of a document. The document is not changed: the function gives a new
2817
+ * document that shares each part without a change.
2818
+ *
2819
+ * @param world - The world of the kinds.
2820
+ * @param doc - The document, as JSON.
2821
+ * @param change - The function that gives the new document of a reference, or `undefined` to keep it.
2822
+ * @param options - The type of a bare root value, and the names that the host binds at the root.
2823
+ * @returns The new document.
2824
+ */
2825
+ function mapRefs(world, doc, change, options = {}) {
2826
+ let out = doc;
2827
+ for (const use of refsOf(world, doc, options)) {
2828
+ if (!use.free) continue;
2829
+ const next = change(use);
2830
+ if (next !== void 0) out = setAt(out, use.at, next);
2831
+ }
2832
+ return out;
2833
+ }
2834
+ /**
2835
+ * This function checks a document before an evaluation, by the contracts of its kinds. It finds the problems
2836
+ * that an evaluation finds when it reaches them:
2837
+ *
2838
+ * - `#KIND?`: a kind that the world does not have.
2839
+ * - `#NOEVAL`: no evaluator.
2840
+ * - `#VALUE!`: a value without the shape of its kind.
2841
+ * - `#PART?`: a value without a type.
2842
+ * - `#DEPTH!`: too much nesting.
2843
+ * - `#REF!` and `#STEP?`: a bad path.
2844
+ * - `#NAME?`: a name that nothing binds.
2845
+ * - `#CAP!`: an effect that the capabilities do not permit.
2846
+ *
2847
+ * @param machine - The world and the code of the evaluators.
2848
+ * @param doc - The document, as JSON.
2849
+ * @param options - The type of a bare root value, the names of the host and the capabilities.
2850
+ * @returns The problems, in the order of the document.
2851
+ */
2852
+ function check(machine, doc, options = {}) {
2853
+ const issues = [];
2854
+ const maxDepth = machine.maxDepth ?? 1e3;
2855
+ const anyName = options.names === "any";
2856
+ const roots = machine.roots ?? ROOTS;
2857
+ const has = (table, name) => table !== void 0 && Object.hasOwn(table, name);
2858
+ walk(machine.world, doc, (v) => {
2859
+ if (v.depth > maxDepth) {
2860
+ issues.push({
2861
+ code: "#DEPTH!",
2862
+ place: v.place,
2863
+ reason: `The nesting is deeper than ${maxDepth} documents.`
2864
+ });
2865
+ return "skip";
2866
+ }
2867
+ const header = v.declared ? {} : v.doc;
2868
+ const answer = ask(machine.world, header, "eval", v.declared ? { declaredType: typeName(v.doc.type) } : {});
2869
+ if (answer.error === "unknown-kind") {
2870
+ issues.push({
2871
+ code: "#KIND?",
2872
+ place: v.place,
2873
+ reason: `The world has no kind ${typeName(v.doc.type)}.`
2874
+ });
2875
+ return "skip";
2876
+ }
2877
+ const name = answer.evaluator;
2878
+ if (name === void 0 || !has(machine.evaluators, name) && !has(machine.runners, name)) issues.push({
2879
+ code: "#NOEVAL",
2880
+ place: v.place,
2881
+ reason: `No rung gives an evaluator for ${typeName(v.doc.type)}.`
2882
+ });
2883
+ if (options.caps !== void 0 && v.kind?.effects !== void 0) {
2884
+ const kinds = v.kind.effects === "type" ? [v.typePath ?? []] : v.kind.effects.map((kind) => kind.split("."));
2885
+ for (const kind of kinds) if (!permits(options.caps, kind)) issues.push({
2886
+ code: "#CAP!",
2887
+ place: v.place,
2888
+ reason: `The capabilities do not permit the effect ${kind.join(".")}.`
2889
+ });
2890
+ }
2891
+ if (!isRef(v.doc)) return void 0;
2892
+ const path = v.doc.value;
2893
+ const root = path[0];
2894
+ if (typeof root !== "string" || !ROOTS.includes(root)) {
2895
+ issues.push({
2896
+ code: "#REF!",
2897
+ place: v.place,
2898
+ reason: `A path starts with a root: ${ROOTS.join(", ")}.`
2899
+ });
2900
+ return "skip";
2901
+ }
2902
+ if (!roots.includes(root)) issues.push({
2903
+ code: "#REF!",
2904
+ place: v.place,
2905
+ reason: `The root ${root} is not open here. The open roots are ${roots.join(", ")}.`
2906
+ });
2907
+ const first = path[1];
2908
+ if (root === "names" && !anyName && isKey(first) && !v.names.has(String(first))) {
2909
+ const later = v.later.has(String(first));
2910
+ issues.push({
2911
+ code: "#NAME?",
2912
+ place: v.place,
2913
+ reason: later ? `The name ${String(first)} is bound by a later step.` : `Nothing binds the name ${String(first)}.`
2914
+ });
2915
+ }
2916
+ path.slice(1).forEach((step) => {
2917
+ if (isKey(step)) return;
2918
+ const found = isEnvelope(step) ? lookupKind(machine.world, step.type) : void 0;
2919
+ const walker = found?.ok === true && Object.hasOwn(found.kind.verbs, "step") ? found.kind.verbs["step"] : void 0;
2920
+ if (walker === void 0 || !has(machine.walkers, walker)) issues.push({
2921
+ code: "#STEP?",
2922
+ place: v.place,
2923
+ reason: "A step is a key, or a document of a kind with the verb step."
2924
+ });
2925
+ });
2926
+ return "skip";
2927
+ }, {
2928
+ ...options.declaredType === void 0 ? {} : { declaredType: options.declaredType },
2929
+ ...Array.isArray(options.names) ? { names: options.names } : {},
2930
+ problem: (problem) => issues.push({
2931
+ code: problem.code,
2932
+ place: problem.place,
2933
+ reason: problem.reason
2934
+ })
2935
+ });
2936
+ return issues;
2937
+ }
2938
+ //#endregion
2939
+ //#region src/formula.ts
2940
+ /** The value of a name whose binding failed. A reference to the name gives the error. */
2941
+ var Held = class {
2942
+ error;
2943
+ constructor(error) {
2944
+ this.error = error;
2945
+ }
2946
+ };
2947
+ const CLOSURE = Symbol.for("coeval.closure");
2948
+ /**
2949
+ * This function tells if a value is a function that the construct `fn` made.
2950
+ *
2951
+ * @param value - The value.
2952
+ * @returns `true` for a closure.
2953
+ */
2954
+ function isClosure(value) {
2955
+ return typeof value === "function" && value[CLOSURE] !== void 0;
2956
+ }
2957
+ /** The standard extensions: the values that JSON cannot write. */
2958
+ const STD_EXT = {
2959
+ undefined: () => ok(void 0),
2960
+ number: (data) => {
2961
+ if (data === "NaN") return ok(NaN);
2962
+ if (data === "Infinity") return ok(Number.POSITIVE_INFINITY);
2963
+ if (data === "-Infinity") return ok(Number.NEGATIVE_INFINITY);
2964
+ if (data === "-0") return ok(-0);
2965
+ return fail("#VALUE!", "The value of ext.number is NaN, Infinity, -Infinity or -0.");
2966
+ }
2967
+ };
2968
+ /** The truth of a value in the rules of JavaScript: `0`, `""`, `null`, `undefined` and `NaN` are false. */
2969
+ function jsTruthy(value) {
2970
+ return Boolean(value);
2971
+ }
2972
+ function strictTruthy(value) {
2973
+ return typeof value === "boolean" ? value : void 0;
2974
+ }
2975
+ function setOwn(target, key, value) {
2976
+ Object.defineProperty(target, key, {
2977
+ value,
2978
+ enumerable: true,
2979
+ writable: true,
2980
+ configurable: true
2981
+ });
2982
+ }
2983
+ function compiled(compile) {
2984
+ return { compile };
2985
+ }
2986
+ function partsOf(value) {
2987
+ return isRecord(value) ? value : {};
2988
+ }
2989
+ const LIT_SUMMARY = "A literal: its value is the result.";
2990
+ /**
2991
+ * This function makes the kinds of the formula kit, for a world. The kinds are the shared constructs of D13 and
2992
+ * the namespaces `ops` and `ext`. A bare value in a position of a term is a literal.
2993
+ *
2994
+ * @returns The entries of the world, by their key.
2995
+ */
2996
+ function formulaKinds() {
2997
+ const term = {
2998
+ shape: "term",
2999
+ of: "lit"
3000
+ };
3001
+ const terms = {
3002
+ shape: "list",
3003
+ of: "lit"
3004
+ };
3005
+ return {
3006
+ lit: {
3007
+ verbs: { eval: "formula.lit" },
3008
+ summary: LIT_SUMMARY
3009
+ },
3010
+ ref: {
3011
+ verbs: { eval: "formula.ref" },
3012
+ summary: "A path from a root of the context. Its result is what the path reaches."
3013
+ },
3014
+ record: {
3015
+ verbs: { eval: "formula.record" },
3016
+ parts: {
3017
+ shape: "map",
3018
+ of: "lit"
3019
+ },
3020
+ summary: "A record: each field is a term."
3021
+ },
3022
+ list: {
3023
+ verbs: { eval: "formula.list" },
3024
+ parts: terms,
3025
+ summary: "A list: each item is a term."
3026
+ },
3027
+ let: {
3028
+ verbs: { eval: "formula.let" },
3029
+ parts: {
3030
+ shape: "record",
3031
+ fields: {
3032
+ bind: {
3033
+ shape: "map",
3034
+ of: "lit"
3035
+ },
3036
+ body: term
3037
+ },
3038
+ required: ["bind", "body"]
3039
+ },
3040
+ scope: [{
3041
+ names: { keysAt: ["bind"] },
3042
+ in: [["body"]]
3043
+ }],
3044
+ summary: "Names for the body. Each binding sees the names around the let, not the other bindings."
3045
+ },
3046
+ fn: {
3047
+ verbs: { eval: "formula.fn" },
3048
+ parts: {
3049
+ shape: "record",
3050
+ fields: { body: term },
3051
+ required: ["params", "body"]
3052
+ },
3053
+ scope: [{
3054
+ names: { listAt: ["params"] },
3055
+ in: [["body"]]
3056
+ }],
3057
+ summary: "A function: its parameters are names for its body."
3058
+ },
3059
+ call: {
3060
+ verbs: { eval: "formula.call" },
3061
+ parts: {
3062
+ shape: "record",
3063
+ fields: {
3064
+ fn: term,
3065
+ args: terms
3066
+ },
3067
+ required: ["fn"]
3068
+ },
3069
+ summary: "It calls a function with the values of its arguments."
3070
+ },
3071
+ if: {
3072
+ verbs: { eval: "formula.if" },
3073
+ parts: {
3074
+ shape: "record",
3075
+ fields: {
3076
+ test: term,
3077
+ then: term,
3078
+ else: term
3079
+ },
3080
+ required: ["test", "then"]
3081
+ },
3082
+ summary: "It evaluates the test, then only the branch that the test selects."
3083
+ },
3084
+ and: {
3085
+ verbs: { eval: "formula.and" },
3086
+ parts: terms,
3087
+ summary: "True when each term is true. It stops at the first false term."
3088
+ },
3089
+ or: {
3090
+ verbs: { eval: "formula.or" },
3091
+ parts: terms,
3092
+ summary: "True when one term is true. It stops at the first true term."
3093
+ },
3094
+ ops: {
3095
+ kinds: {},
3096
+ fallback: {
3097
+ verbs: { eval: "formula.op" },
3098
+ parts: terms,
3099
+ summary: "An operation of the language, applied to the values of its terms."
3100
+ }
3101
+ },
3102
+ ext: {
3103
+ kinds: {},
3104
+ fallback: {
3105
+ verbs: { eval: "formula.ext" },
3106
+ summary: "An extension of the language. Its value is data."
3107
+ }
3108
+ }
3109
+ };
3110
+ }
3111
+ /**
3112
+ * This function makes the evaluators of the formula kit for one language. Each evaluator compiles its document
3113
+ * one time into code.
3114
+ *
3115
+ * @param profile - The operations and the rules of the language.
3116
+ * @returns The evaluators, by their name.
3117
+ */
3118
+ function formulaEvaluators(profile = {}) {
3119
+ const truthy = profile.truthy ?? strictTruthy;
3120
+ const otherwise = ok(profile.otherwise ?? null);
3121
+ const logicValue = profile.logic === "value";
3122
+ const omit = profile.omit;
3123
+ const missing = profile.missing;
3124
+ const check = profile.check;
3125
+ const lookup = (name) => {
3126
+ const ops = profile.ops;
3127
+ if (ops === void 0) return void 0;
3128
+ if (typeof ops === "function") return ops(name);
3129
+ return Object.hasOwn(ops, name) ? ops[name] : void 0;
3130
+ };
3131
+ const notTruth = (what) => fail("#VALUE!", `The ${what} must give a truth value.`);
3132
+ return {
3133
+ "formula.lit": compiled(({ doc }) => {
3134
+ const result = ok(doc.value);
3135
+ return () => result;
3136
+ }),
3137
+ "formula.ref": compiled(({ doc }) => {
3138
+ const path = doc.value;
3139
+ if (!Array.isArray(path) || path.length === 0) {
3140
+ const bad = fail("#VALUE!", "The value of ref is a path: a list that starts with a root, for example [\"names\", \"a\"].");
3141
+ return () => bad;
3142
+ }
3143
+ const head = path[0] === "names" && typeof path[1] === "string" ? path[1] : void 0;
3144
+ return (ctx, rt) => {
3145
+ if (head !== void 0 && Object.hasOwn(ctx.names, head)) {
3146
+ const bound = ctx.names[head];
3147
+ if (bound instanceof Held) return {
3148
+ ok: false,
3149
+ error: bound.error
3150
+ };
3151
+ }
3152
+ const found = rt.resolve(ctx, path);
3153
+ if (!found.ok) return fail(found.code, found.reason);
3154
+ const value = valueOfThing(found.thing);
3155
+ if (value instanceof Held) return {
3156
+ ok: false,
3157
+ error: value.error
3158
+ };
3159
+ if (missing !== void 0 && missing(value)) return fail("#N/A", "The path reaches no value.");
3160
+ return ok(value);
3161
+ };
3162
+ }),
3163
+ "formula.record": compiled(({ value }) => {
3164
+ const fields = Object.entries(partsOf(value));
3165
+ return (ctx, rt) => {
3166
+ const out = {};
3167
+ for (const [key, part] of fields) {
3168
+ const result = part.code(ctx, rt);
3169
+ if (!result.ok) {
3170
+ if (omit !== void 0 && omit(result.error)) continue;
3171
+ return result;
3172
+ }
3173
+ setOwn(out, key, result.value);
3174
+ }
3175
+ return ok(out);
3176
+ };
3177
+ }),
3178
+ "formula.list": compiled(({ parts }) => (ctx, rt) => {
3179
+ const out = new Array(parts.length);
3180
+ for (let index = 0; index < parts.length; index += 1) {
3181
+ const result = parts[index].code(ctx, rt);
3182
+ if (!result.ok) return result;
3183
+ out[index] = result.value;
3184
+ }
3185
+ return ok(out);
3186
+ }),
3187
+ "formula.let": compiled(({ value }) => {
3188
+ const record = partsOf(value);
3189
+ const bindings = Object.entries(partsOf(record["bind"]));
3190
+ const body = record["body"];
3191
+ return (ctx, rt) => {
3192
+ const names = { ...ctx.names };
3193
+ for (const [name, part] of bindings) {
3194
+ const result = part.code(ctx, rt);
3195
+ setOwn(names, name, result.ok ? result.value : new Held(result.error));
3196
+ }
3197
+ return body.code({
3198
+ ...ctx,
3199
+ names
3200
+ }, rt);
3201
+ };
3202
+ }),
3203
+ "formula.fn": compiled(({ value, data }) => {
3204
+ const params = isRecord(data) ? data["params"] : void 0;
3205
+ if (!Array.isArray(params) || !params.every((param) => typeof param === "string") || new Set(params).size !== params.length) {
3206
+ const bad = fail("#VALUE!", "The params of fn are a list of different texts.");
3207
+ return () => bad;
3208
+ }
3209
+ const names = params;
3210
+ const body = partsOf(value)["body"];
3211
+ return (ctx, rt) => {
3212
+ const closure = (...args) => {
3213
+ const scope = { ...ctx.names };
3214
+ names.forEach((name, index) => setOwn(scope, name, args[index]));
3215
+ const result = body.code({
3216
+ ...ctx,
3217
+ names: scope
3218
+ }, rt);
3219
+ if (!result.ok) throw new Failure(result.error);
3220
+ return result.value;
3221
+ };
3222
+ Object.defineProperty(closure, CLOSURE, { value: { params: names } });
3223
+ return ok(closure);
3224
+ };
3225
+ }),
3226
+ "formula.call": compiled(({ value }) => {
3227
+ const record = partsOf(value);
3228
+ const fnPart = record["fn"];
3229
+ const args = Array.isArray(record["args"]) ? record["args"] : [];
3230
+ return (ctx, rt) => {
3231
+ const fn = fnPart.code(ctx, rt);
3232
+ if (!fn.ok) return fn;
3233
+ if (typeof fn.value !== "function") return fail("#VALUE!", "The fn of call must give a function.");
3234
+ const values = new Array(args.length);
3235
+ for (let index = 0; index < args.length; index += 1) {
3236
+ const result = args[index].code(ctx, rt);
3237
+ if (!result.ok) return result;
3238
+ values[index] = result.value;
3239
+ }
3240
+ try {
3241
+ return ok(fn.value(...values));
3242
+ } catch (error) {
3243
+ return thrown(error, "the function of call");
3244
+ }
3245
+ };
3246
+ }),
3247
+ "formula.if": compiled(({ value }) => {
3248
+ const record = partsOf(value);
3249
+ const test = record["test"];
3250
+ const then = record["then"];
3251
+ const other = record["else"];
3252
+ return (ctx, rt) => {
3253
+ const chosen = test.code(ctx, rt);
3254
+ if (!chosen.ok) return chosen;
3255
+ const truth = truthy(chosen.value);
3256
+ if (truth === void 0) return notTruth("test of if");
3257
+ if (truth) return then.code(ctx, rt);
3258
+ return other === void 0 ? otherwise : other.code(ctx, rt);
3259
+ };
3260
+ }),
3261
+ "formula.and": compiled(({ parts }) => (ctx, rt) => {
3262
+ let last = ok(true);
3263
+ for (const part of parts) {
3264
+ last = part.code(ctx, rt);
3265
+ if (!last.ok) return last;
3266
+ const truth = truthy(last.value);
3267
+ if (truth === void 0) return notTruth("term of and");
3268
+ if (!truth) return logicValue ? last : ok(false);
3269
+ }
3270
+ return logicValue ? last : ok(true);
3271
+ }),
3272
+ "formula.or": compiled(({ parts }) => (ctx, rt) => {
3273
+ let last = ok(false);
3274
+ for (const part of parts) {
3275
+ last = part.code(ctx, rt);
3276
+ if (!last.ok) return last;
3277
+ const truth = truthy(last.value);
3278
+ if (truth === void 0) return notTruth("term of or");
3279
+ if (truth) return logicValue ? last : ok(true);
3280
+ }
3281
+ return logicValue ? last : ok(false);
3282
+ }),
3283
+ "formula.op": compiled(({ typePath, parts }) => {
3284
+ const name = typePath.slice(1).join(".");
3285
+ const lazy = profile.lazy !== void 0 && Object.hasOwn(profile.lazy, name) ? profile.lazy[name] : void 0;
3286
+ if (lazy !== void 0) return (ctx, rt) => {
3287
+ try {
3288
+ return lazy(parts.map((part) => () => part.code(ctx, rt)));
3289
+ } catch (error) {
3290
+ return thrown(error, `the operation ${name}`);
3291
+ }
3292
+ };
3293
+ const fn = lookup(name);
3294
+ if (fn === void 0) {
3295
+ const unknown = fail("#NAME?", `The language has no operation ${name}.`);
3296
+ return () => unknown;
3297
+ }
3298
+ const done = (out) => check === void 0 ? ok(out) : check(out, name) ?? ok(out);
3299
+ const failed = (error) => thrown(error, `the operation ${name}`);
3300
+ if (parts.length === 1) {
3301
+ const [a] = parts;
3302
+ return (ctx, rt) => {
3303
+ const x = a.code(ctx, rt);
3304
+ if (!x.ok) return x;
3305
+ try {
3306
+ return done(fn(x.value));
3307
+ } catch (error) {
3308
+ return failed(error);
3309
+ }
3310
+ };
3311
+ }
3312
+ if (parts.length === 2) {
3313
+ const [a, b] = parts;
3314
+ return (ctx, rt) => {
3315
+ const x = a.code(ctx, rt);
3316
+ if (!x.ok) return x;
3317
+ const y = b.code(ctx, rt);
3318
+ if (!y.ok) return y;
3319
+ try {
3320
+ return done(fn(x.value, y.value));
3321
+ } catch (error) {
3322
+ return failed(error);
3323
+ }
3324
+ };
3325
+ }
3326
+ return (ctx, rt) => {
3327
+ const values = new Array(parts.length);
3328
+ for (let index = 0; index < parts.length; index += 1) {
3329
+ const result = parts[index].code(ctx, rt);
3330
+ if (!result.ok) return result;
3331
+ values[index] = result.value;
3332
+ }
3333
+ try {
3334
+ return done(fn(...values));
3335
+ } catch (error) {
3336
+ return failed(error);
3337
+ }
3338
+ };
3339
+ }),
3340
+ "formula.ext": compiled(({ typePath, doc }) => {
3341
+ const name = typePath.slice(1).join(".");
3342
+ const handler = (profile.ext !== void 0 && Object.hasOwn(profile.ext, name) ? profile.ext[name] : void 0) ?? (Object.hasOwn(STD_EXT, name) ? STD_EXT[name] : void 0);
3343
+ if (handler === void 0) {
3344
+ const unknown = fail("#NAME?", `The language has no extension ${name}.`);
3345
+ return () => unknown;
3346
+ }
3347
+ return () => {
3348
+ try {
3349
+ return handler(doc.value);
3350
+ } catch (error) {
3351
+ return thrown(error, `the extension ${name}`);
3352
+ }
3353
+ };
3354
+ })
3355
+ };
3356
+ }
3357
+ /**
3358
+ * This function makes a machine for a formula language. The machine has the kinds and the evaluators of the
3359
+ * formula kit. It also has the kinds, the evaluators and the walkers that the language adds.
3360
+ *
3361
+ * @param profile - The operations and the rules of the language.
3362
+ * @param more - The parts of a machine that the language adds. Its kinds win over the kinds of the kit.
3363
+ * @returns The machine.
3364
+ */
3365
+ function formulaMachine(profile = {}, more = {}) {
3366
+ return {
3367
+ ...more,
3368
+ world: {
3369
+ kinds: {
3370
+ ...formulaKinds(),
3371
+ ...more.world?.kinds
3372
+ },
3373
+ defaults: { ...more.world?.defaults }
3374
+ },
3375
+ evaluators: {
3376
+ ...formulaEvaluators(profile),
3377
+ ...more.evaluators
3378
+ }
3379
+ };
3380
+ }
3381
+ //#endregion
3382
+ export { CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_WALKERS, STD_WORLD, THING, ask, capKeys, check, checkSteps, classify, context, dataEdges, derefStep, describe, docEquals, effectEdges, entriesAsJson, evaluate, fail, failures, field, flatten, formulaEvaluators, formulaKinds, formulaMachine, fromJson, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, narrow, nearestWalker, nodeAt, ok, pathMatches, permits, placed, planner, prepare, ref, refsOf, resolve, run, splitParts, stored, tag, thrown, toJson, typeName, typePath, typed, valueOfThing, walk, worldThing };
1855
3383
 
1856
3384
  //# sourceMappingURL=index.js.map