@mark1russell7/coeval 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js ADDED
@@ -0,0 +1,3378 @@
1
+ //#region src/doc.ts
2
+ /** The keys that an envelope can have. */
3
+ const ENVELOPE_KEYS = [
4
+ "type",
5
+ "value",
6
+ "name",
7
+ "own"
8
+ ];
9
+ /**
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.
12
+ *
13
+ * @param value - The value to examine.
14
+ * @returns `true` for a plain record.
15
+ */
16
+ function isRecord(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;
20
+ }
21
+ /**
22
+ * This function reads a field of a record. It reads only the own fields, thus `constructor` is not a field.
23
+ *
24
+ * @param record - The record.
25
+ * @param key - The name of the field.
26
+ * @returns The value of the field, or `undefined` when the record has no such field.
27
+ */
28
+ function field(record, key) {
29
+ return Object.hasOwn(record, key) ? record[key] : void 0;
30
+ }
31
+ /**
32
+ * This function tells if a value is a key of a path: a text, or a whole number from 0.
33
+ *
34
+ * @param value - The value to examine.
35
+ * @returns `true` for a key.
36
+ */
37
+ function isKey(value) {
38
+ return typeof value === "string" || typeof value === "number" && Number.isInteger(value) && value >= 0;
39
+ }
40
+ /**
41
+ * This function tells if a value is a reference to a type. The reference is a text, a list of keys, or a document
42
+ * of type `ref` whose path starts at the root `world`.
43
+ *
44
+ * @param value - The value to examine.
45
+ * @returns `true` for a reference to a type.
46
+ */
47
+ function isTypeRef(value) {
48
+ if (typeof value === "string") return value !== "";
49
+ if (Array.isArray(value)) return value.length > 0 && value.every(isKey);
50
+ if (!isRecord(value)) return false;
51
+ const path = field(value, "value");
52
+ return field(value, "type") === "ref" && Array.isArray(path) && path[0] === "world";
53
+ }
54
+ /**
55
+ * This function gives the path of a reference to a type, from the root of the world.
56
+ *
57
+ * @param type - The reference.
58
+ * @returns The keys from the root of the world, or `undefined` for a computed step that has no fixed key.
59
+ */
60
+ function typePath(type) {
61
+ if (typeof type === "string") return [type];
62
+ if (Array.isArray(type)) return type;
63
+ const steps = type.value.slice(1);
64
+ return steps.every(isKey) ? steps : void 0;
65
+ }
66
+ /**
67
+ * This function gives the text of a type for a reader: its keys, joined with a dot.
68
+ *
69
+ * @param type - The reference to the type.
70
+ * @returns The text, for example `git.push`.
71
+ */
72
+ function typeName(type) {
73
+ return typePath(type)?.join(".") ?? "(computed)";
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
+ }
82
+ /**
83
+ * This function tells if a value has the form of an envelope. That form is a record with a reference to a type
84
+ * and a `value`. It has no other keys than the keys of an envelope. A contract decides where an envelope is
85
+ * expected. This function only checks the form.
86
+ *
87
+ * @param value - The value to examine.
88
+ * @returns `true` when the value has the form of an envelope.
89
+ */
90
+ function isEnvelope(value) {
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
+ };
117
+ }
118
+ /**
119
+ * This function makes an envelope.
120
+ *
121
+ * @param type - The reference to the kind.
122
+ * @param value - The value.
123
+ * @param header - The optional name and own entries.
124
+ * @returns The envelope.
125
+ */
126
+ function typed(type, value, header = {}) {
127
+ return {
128
+ type,
129
+ value,
130
+ ...header.name === void 0 ? {} : { name: header.name },
131
+ ...header.own === void 0 ? {} : { own: header.own }
132
+ };
133
+ }
134
+ /**
135
+ * This function makes a reference document: a path from a root of the context.
136
+ *
137
+ * @param path - The steps. The first step is a root: `self`, `names`, `world`, `event` or `caps`.
138
+ * @returns The document of type `ref`.
139
+ */
140
+ function ref(...path) {
141
+ return typed("ref", path);
142
+ }
143
+ /**
144
+ * This function gives an envelope as a JSON record, for a store or for a view.
145
+ *
146
+ * @param doc - The envelope.
147
+ * @returns The same envelope as JSON.
148
+ */
149
+ function toJson(doc) {
150
+ return {
151
+ type: doc.type,
152
+ value: doc.value,
153
+ ...doc.name === void 0 ? {} : { name: doc.name },
154
+ ...doc.own === void 0 ? {} : { own: doc.own }
155
+ };
156
+ }
157
+ /**
158
+ * This function reads an envelope from JSON. A value without the form of an envelope gives `undefined`.
159
+ *
160
+ * @param value - The JSON value.
161
+ * @returns The envelope, or `undefined`.
162
+ */
163
+ function fromJson(value) {
164
+ if (!isEnvelope(value)) return void 0;
165
+ const own = field(value, "own");
166
+ const ownEntries = {};
167
+ if (isRecord(own)) {
168
+ for (const [verb, name] of Object.entries(own)) if (typeof name === "string") ownEntries[verb] = name;
169
+ }
170
+ const name = field(value, "name");
171
+ return typed(field(value, "type"), field(value, "value") ?? null, {
172
+ ...typeof name === "string" ? { name } : {},
173
+ ...isRecord(own) ? { own: ownEntries } : {}
174
+ });
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
+ }
319
+ //#endregion
320
+ //#region src/world.ts
321
+ /**
322
+ * This function tells if an entry of the world is a kind.
323
+ *
324
+ * @param entry - The entry.
325
+ * @returns `true` for a kind.
326
+ */
327
+ function isKind(entry) {
328
+ return entry !== void 0 && Object.hasOwn(entry, "verbs");
329
+ }
330
+ /**
331
+ * This function tells if an entry of the world is an alias.
332
+ *
333
+ * @param entry - The entry.
334
+ * @returns `true` for an alias.
335
+ */
336
+ function isAlias(entry) {
337
+ return entry !== void 0 && entry.type === "ref" && Array.isArray(entry.value);
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
+ }
348
+ function entryAt(entries, key) {
349
+ const name = String(key);
350
+ return Object.hasOwn(entries, name) ? entries[name] : void 0;
351
+ }
352
+ /**
353
+ * This function finds the kind that a type names. It walks the path from the root of the world, one key at a
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.
356
+ *
357
+ * @param world - The world.
358
+ * @param type - The reference to the type.
359
+ * @returns The kind and the path of the kind after each alias, or the reason of a failure.
360
+ */
361
+ function lookupKind(world, type) {
362
+ const start = typePath(type);
363
+ if (start === void 0) return {
364
+ ok: false,
365
+ reason: "computed",
366
+ path: []
367
+ };
368
+ let path = start;
369
+ let aliases = 0;
370
+ for (;;) {
371
+ let entries = world.kinds;
372
+ let fallback;
373
+ let entry;
374
+ let followed = false;
375
+ for (let index = 0; index < path.length; index += 1) {
376
+ const key = path[index];
377
+ entry = entryAt(entries, key);
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
+ }
391
+ if (isAlias(entry)) {
392
+ aliases += 1;
393
+ if (aliases > 32) return {
394
+ ok: false,
395
+ reason: "cycle",
396
+ path
397
+ };
398
+ const target = entry.value.slice(1);
399
+ if (entry.value[0] !== "world" || !target.every((item) => typeof item === "string" || typeof item === "number")) return {
400
+ ok: false,
401
+ reason: "not-a-kind",
402
+ path
403
+ };
404
+ path = [...target, ...path.slice(index + 1)];
405
+ followed = true;
406
+ break;
407
+ }
408
+ if (index < path.length - 1) {
409
+ if (!isNamespace(entry)) return {
410
+ ok: false,
411
+ reason: "not-a-kind",
412
+ path
413
+ };
414
+ entries = entry.kinds;
415
+ fallback = entry.fallback;
416
+ }
417
+ }
418
+ if (followed) continue;
419
+ if (!isKind(entry)) return {
420
+ ok: false,
421
+ reason: "not-a-kind",
422
+ path
423
+ };
424
+ return {
425
+ ok: true,
426
+ kind: entry,
427
+ path,
428
+ aliases
429
+ };
430
+ }
431
+ }
432
+ /**
433
+ * This function gives the entries of a world as a JSON record, so that a reference can walk it. A kind is a
434
+ * record with its verbs, an alias is a reference document, and a namespace is a record of its entries.
435
+ *
436
+ * @param entries - The entries.
437
+ * @returns The JSON form.
438
+ */
439
+ function entriesAsJson(entries) {
440
+ const out = {};
441
+ for (const [key, entry] of Object.entries(entries)) if (isAlias(entry)) out[key] = {
442
+ type: "ref",
443
+ value: entry.value
444
+ };
445
+ else if (isKind(entry)) out[key] = JSON.parse(JSON.stringify(entry));
446
+ else out[key] = entriesAsJson(entry.kinds);
447
+ return out;
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
+ }
457
+ /**
458
+ * This function makes the document of a kind, so that a view can draw it as each other document.
459
+ *
460
+ * @param name - The name of the kind.
461
+ * @param kind - The kind.
462
+ * @returns The document of type `kind`.
463
+ */
464
+ function kindDocument(name, kind) {
465
+ return {
466
+ type: "kind",
467
+ value: {
468
+ name,
469
+ verbs: { ...kind.verbs },
470
+ parts: describeShape(kind.parts),
471
+ side: kind.side ?? "derive",
472
+ summary: kind.summary ?? ""
473
+ }
474
+ };
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
+ }
485
+ //#endregion
486
+ //#region src/ladder.ts
487
+ /** The rungs of the ladder, in the order that a question visits them. */
488
+ const RUNGS = [
489
+ "own",
490
+ "kind",
491
+ "default"
492
+ ];
493
+ function entryOf$1(verbs, verb) {
494
+ return verbs !== void 0 && Object.hasOwn(verbs, verb) ? verbs[verb] : void 0;
495
+ }
496
+ /**
497
+ * This function finds the kind that a type names, and follows each alias. It gives `undefined` for an unknown
498
+ * type.
499
+ *
500
+ * @param world - The world.
501
+ * @param type - The reference to the type.
502
+ * @returns The kind, or `undefined`.
503
+ */
504
+ function kindOf(world, type) {
505
+ const found = lookupKind(world, type);
506
+ return found.ok ? found.kind : void 0;
507
+ }
508
+ /**
509
+ * This function finds a kind by a text type. It is the same as `kindOf` for a one-key reference.
510
+ *
511
+ * @param world - The world.
512
+ * @param name - The text of the type.
513
+ * @returns The kind, or `undefined`.
514
+ */
515
+ function kindNamed(world, name) {
516
+ return kindOf(world, name);
517
+ }
518
+ /**
519
+ * This function asks a document a question, and gives the evaluator that answers it.
520
+ *
521
+ * The question visits the rungs in order: the own entry of the envelope, then its kind, then the default of the
522
+ * world. The type comes from the envelope, or else from the type that the position declares. The type is a
523
+ * reference into the world. The ladder does not guess a type from the shape of a value.
524
+ *
525
+ * @param world - The kinds and the defaults.
526
+ * @param doc - The header of the document: an envelope, or `{}` for a bare value.
527
+ * @param verb - The verb of the question, for example `eval` or `view`.
528
+ * @param options - The type that the position declares.
529
+ * @returns The answer, with the record of each rung that the question visited.
530
+ */
531
+ function ask(world, doc, verb, options = {}) {
532
+ const steps = [];
533
+ const own = entryOf$1(doc.own, verb);
534
+ if (own !== void 0) {
535
+ steps.push({
536
+ rung: "own",
537
+ outcome: "answers",
538
+ evaluator: own
539
+ });
540
+ return {
541
+ verb,
542
+ evaluator: own,
543
+ rung: "own",
544
+ steps
545
+ };
546
+ }
547
+ steps.push({
548
+ rung: "own",
549
+ outcome: "no-entry"
550
+ });
551
+ const type = doc.type ?? options.declaredType;
552
+ if (type === void 0) steps.push({
553
+ rung: "kind",
554
+ outcome: "skipped"
555
+ });
556
+ else {
557
+ const source = doc.type === void 0 ? "container" : "document";
558
+ const name = typeName(type);
559
+ const kind = kindOf(world, type);
560
+ if (kind === void 0) {
561
+ steps.push({
562
+ rung: "kind",
563
+ outcome: "unknown-kind",
564
+ kind: name,
565
+ source
566
+ });
567
+ return {
568
+ verb,
569
+ error: "unknown-kind",
570
+ steps
571
+ };
572
+ }
573
+ const entry = entryOf$1(kind.verbs, verb);
574
+ if (entry !== void 0) {
575
+ steps.push({
576
+ rung: "kind",
577
+ outcome: "answers",
578
+ evaluator: entry,
579
+ kind: name,
580
+ source
581
+ });
582
+ return {
583
+ verb,
584
+ evaluator: entry,
585
+ rung: "kind",
586
+ steps
587
+ };
588
+ }
589
+ steps.push({
590
+ rung: "kind",
591
+ outcome: "no-entry",
592
+ kind: name,
593
+ source
594
+ });
595
+ }
596
+ const fallback = entryOf$1(world.defaults, verb);
597
+ if (fallback !== void 0) {
598
+ steps.push({
599
+ rung: "default",
600
+ outcome: "answers",
601
+ evaluator: fallback
602
+ });
603
+ return {
604
+ verb,
605
+ evaluator: fallback,
606
+ rung: "default",
607
+ steps
608
+ };
609
+ }
610
+ steps.push({
611
+ rung: "default",
612
+ outcome: "no-entry"
613
+ });
614
+ return {
615
+ verb,
616
+ error: "no-answer",
617
+ steps
618
+ };
619
+ }
620
+ //#endregion
621
+ //#region src/deref.ts
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");
627
+ /**
628
+ * This function tells if a thing is a host thing.
629
+ *
630
+ * @param thing - The thing.
631
+ * @returns `true` for a host thing.
632
+ */
633
+ function isHostThing(thing) {
634
+ return typeof thing === "object" && thing !== null && thing[THING] === true;
635
+ }
636
+ /** The roots of a context. Each path starts with one of them. */
637
+ const ROOTS = [
638
+ "self",
639
+ "names",
640
+ "world",
641
+ "event",
642
+ "caps"
643
+ ];
644
+ function entryOf(verbs, verb) {
645
+ return verbs !== void 0 && Object.hasOwn(verbs, verb) ? verbs[verb] : void 0;
646
+ }
647
+ /** The own fields of a value: the items and the length of a list, and the own properties of an object. */
648
+ function plainStep(thing, key) {
649
+ if (Array.isArray(thing)) {
650
+ if (key === "length") return {
651
+ found: true,
652
+ value: thing.length
653
+ };
654
+ const index = typeof key === "number" ? key : /^\d+$/.test(key) ? Number(key) : -1;
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;
666
+ }
667
+ }
668
+ /**
669
+ * This function answers one step of a path on a thing, through the ladder of the verb `deref`. A host thing
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.
672
+ *
673
+ * @param nav - The world and the code of the evaluators.
674
+ * @param thing - The thing that the path reached.
675
+ * @param key - The key of the step.
676
+ * @returns What the step reaches and the rung that answered, or `undefined` when it reaches nothing.
677
+ */
678
+ function derefStep(nav, thing, key) {
679
+ if (isHostThing(thing)) {
680
+ const reached = thing.deref(key);
681
+ return reached === void 0 ? void 0 : {
682
+ thing: reached,
683
+ by: "own"
684
+ };
685
+ }
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;
690
+ const ownCode = own === void 0 ? void 0 : nav.derefs?.[own];
691
+ if (ownCode !== void 0) {
692
+ const reached = ownCode(envelope, key);
693
+ return reached === void 0 ? void 0 : {
694
+ thing: reached,
695
+ by: "own"
696
+ };
697
+ }
698
+ const found = lookupKind(nav.world, envelope.type);
699
+ const name = found.ok ? entryOf(found.kind.verbs, "deref") : void 0;
700
+ const code = name === void 0 ? void 0 : nav.derefs?.[name];
701
+ if (code !== void 0) {
702
+ const reached = code(envelope, key);
703
+ return reached === void 0 ? void 0 : {
704
+ thing: reached,
705
+ by: "kind"
706
+ };
707
+ }
708
+ const reached = plainStep(envelope.value, key);
709
+ return reached === void 0 ? void 0 : {
710
+ thing: reached.value,
711
+ by: "default"
712
+ };
713
+ }
714
+ const reached = plainStep(thing, key);
715
+ return reached === void 0 ? void 0 : {
716
+ thing: reached.value,
717
+ by: "default"
718
+ };
719
+ }
720
+ /**
721
+ * This function tells if a thing is a reference document: an envelope of the type `ref` with a list for its path.
722
+ *
723
+ * @param thing - The thing.
724
+ * @returns `true` for a reference document.
725
+ */
726
+ function isRef(thing) {
727
+ if (!isEnvelope(thing) || !Array.isArray(thing.value)) return false;
728
+ const path = typePath(thing.type);
729
+ return path !== void 0 && path.length === 1 && path[0] === "ref";
730
+ }
731
+ const worlds = /* @__PURE__ */ new WeakMap();
732
+ /**
733
+ * This function makes the thing of the root `world`: the entries of the world as JSON. An alias is a reference
734
+ * document in it, thus a walk through the world follows each alias (collapse). The thing of a world is made one
735
+ * time.
736
+ *
737
+ * @param world - The world.
738
+ * @returns The thing.
739
+ */
740
+ function worldThing(world) {
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;
747
+ }
748
+ /**
749
+ * This function makes a context. The world comes from the navigator when the call gives none.
750
+ *
751
+ * @param parts - The roots to set.
752
+ * @returns The context.
753
+ */
754
+ function context(parts = {}) {
755
+ return {
756
+ self: parts.self ?? null,
757
+ names: parts.names ?? {},
758
+ world: parts.world ?? null,
759
+ event: parts.event ?? null,
760
+ caps: parts.caps ?? []
761
+ };
762
+ }
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
+ let here = ctx[root];
779
+ const plain = nav.plain === true;
780
+ for (let index = 1; index < path.length; index += 1) {
781
+ if (typeof here !== "object" || here === null) return void 0;
782
+ if (!plain && mayBeRef(here)) return void 0;
783
+ if (here[THING] === true) return void 0;
784
+ const step = path[index];
785
+ if (Array.isArray(here)) {
786
+ if (typeof step !== "number" || !Number.isInteger(step) || step < 0 || step >= here.length) return void 0;
787
+ here = here[step];
788
+ continue;
789
+ }
790
+ if (typeof step !== "string" || !Object.hasOwn(here, step)) return void 0;
791
+ if (!plain && isEnvelope(here)) return void 0;
792
+ here = here[step];
793
+ }
794
+ if (!plain && mayBeRef(here)) return void 0;
795
+ return {
796
+ ok: true,
797
+ thing: here,
798
+ hops: NO_HOPS
799
+ };
800
+ }
801
+ /**
802
+ * This function resolves a path from a context, and records each hop.
803
+ *
804
+ * 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
805
+ * of the verb `deref` on the thing that the path reached. A step document is evaluated by the walker of its kind,
806
+ * with the thing that the path reached. After each hop, a reference document is followed until the path reaches
807
+ * something else (collapse), except in a plain navigation.
808
+ *
809
+ * The codes of a failure:
810
+ *
811
+ * - `#REF!`: the path has no root, or a root that is not open here.
812
+ * - `#NAME?`: the root `names` has no such name.
813
+ * - `#N/A`: a step reaches nothing.
814
+ * - `#STEP?`: a step is not a key and not a step document.
815
+ * - `#CYCLE!`: the references form a cycle.
816
+ *
817
+ * @param nav - The world and the code of the evaluators.
818
+ * @param ctx - The context.
819
+ * @param path - The steps.
820
+ * @param options - `hops: false` does not record the hops.
821
+ * @returns What the path reached and each hop, or an error.
822
+ */
823
+ function resolve(nav, ctx, path, options = {}) {
824
+ if (options.hops === false) {
825
+ const quick = resolveQuick(nav, ctx, path);
826
+ if (quick !== void 0) return quick;
827
+ }
828
+ const hops = options.hops === false ? void 0 : [];
829
+ const done = hops ?? NO_HOPS;
830
+ const failed = (code, reason) => ({
831
+ ok: false,
832
+ code,
833
+ reason,
834
+ hops: done
835
+ });
836
+ let collapses = 0;
837
+ const collapse = (thing) => {
838
+ let here = thing;
839
+ if (nav.plain === true) return {
840
+ ok: true,
841
+ thing: here,
842
+ hops: done
843
+ };
844
+ while (isRef(here)) {
845
+ collapses += 1;
846
+ if (collapses > MAX_COLLAPSE) return failed("#CYCLE!", "The references form a cycle.");
847
+ const inner = walk(here.value);
848
+ if (!inner.ok) return inner;
849
+ here = inner.thing;
850
+ hops?.push({
851
+ step: "collapse",
852
+ thing: here,
853
+ by: "collapse"
854
+ });
855
+ }
856
+ return {
857
+ ok: true,
858
+ thing: here,
859
+ hops: done
860
+ };
861
+ };
862
+ const walk = (steps) => {
863
+ const root = steps[0];
864
+ if (typeof root !== "string" || !ROOTS.includes(root)) return failed("#REF!", `A path starts with a root: ${ROOTS.join(", ")}.`);
865
+ 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(", ")}.`);
866
+ let here = ctx[root];
867
+ hops?.push({
868
+ step: root,
869
+ thing: here,
870
+ by: "root"
871
+ });
872
+ const first = collapse(here);
873
+ if (!first.ok) return first;
874
+ here = first.thing;
875
+ for (let index = 1; index < steps.length; index += 1) {
876
+ const step = steps[index];
877
+ if (isHostThing(here) && here.resolve !== void 0 && steps.slice(index).every(isKey)) {
878
+ const keys = steps.slice(index);
879
+ const reached = here.resolve(keys);
880
+ if (reached === void 0) return failed("#N/A", `The path ${keys.join(".")} reaches nothing.`);
881
+ hops?.push({
882
+ step: keys,
883
+ thing: reached.thing,
884
+ by: "own"
885
+ });
886
+ return collapse(reached.thing);
887
+ }
888
+ if (isKey(step)) {
889
+ const reached = derefStep(nav, here, step);
890
+ if (reached === void 0) {
891
+ if (root === "names" && index === 1) return failed("#NAME?", `The context has no name ${String(step)}.`);
892
+ return failed("#N/A", `The step ${String(step)} reaches nothing.`);
893
+ }
894
+ hops?.push({
895
+ step,
896
+ thing: reached.thing,
897
+ by: reached.by
898
+ });
899
+ here = reached.thing;
900
+ } else {
901
+ const envelope = isEnvelope(step) ? step : void 0;
902
+ const found = envelope === void 0 ? void 0 : lookupKind(nav.world, envelope.type);
903
+ const name = found?.ok === true ? entryOf(found.kind.verbs, "step") : void 0;
904
+ const walker = name === void 0 ? void 0 : nav.walkers?.[name];
905
+ 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.");
906
+ const result = walker({
907
+ data: envelope.value,
908
+ here,
909
+ context: ctx,
910
+ deref: (thing, key) => derefStep(nav, thing, key)?.thing
911
+ });
912
+ if (!result.ok) return failed("#N/A", result.reason);
913
+ hops?.push({
914
+ step: envelope,
915
+ thing: result.thing,
916
+ by: "step"
917
+ });
918
+ here = result.thing;
919
+ }
920
+ const next = collapse(here);
921
+ if (!next.ok) return next;
922
+ here = next.thing;
923
+ }
924
+ return {
925
+ ok: true,
926
+ thing: here,
927
+ hops: done
928
+ };
929
+ };
930
+ if (path.length === 0) return failed("#REF!", "A path has at least one step: its root.");
931
+ return walk(path);
932
+ }
933
+ /**
934
+ * This function gives the value of a thing: the value that a host thing gives, or else the thing itself.
935
+ *
936
+ * @param thing - The thing.
937
+ * @returns The value.
938
+ */
939
+ function valueOfThing(thing) {
940
+ return isHostThing(thing) ? thing.toJson() : thing;
941
+ }
942
+ //#endregion
943
+ //#region src/classify.ts
944
+ function isStrictSuperset(larger, smaller) {
945
+ return larger.length > smaller.length && smaller.every((name) => larger.includes(name));
946
+ }
947
+ /**
948
+ * This function proposes a kind for a value without a type, from the fields that the value has.
949
+ * A shape applies when the value has each field of the shape. The most specific shapes are the ones that
950
+ * no other applicable shape contains. A host uses a classifier when data comes in, and then tags the data.
951
+ * The ladder does not call a classifier.
952
+ *
953
+ * @param shapes - The shapes to examine.
954
+ * @param value - The value without a type.
955
+ * @returns The proposed kind, or an error with the candidates.
956
+ */
957
+ function classify(shapes, value) {
958
+ const applicable = isRecord(value) ? shapes.filter((shape) => shape.fields.every((name) => field(value, name) !== void 0)) : [];
959
+ const best = applicable.filter((shape) => !applicable.some((other) => isStrictSuperset(other.fields, shape.fields)));
960
+ const candidates = best.map((shape) => shape.name);
961
+ const only = best.length === 1 ? best[0] : void 0;
962
+ if (only !== void 0) return {
963
+ kind: only.kind,
964
+ candidates
965
+ };
966
+ return {
967
+ candidates,
968
+ error: best.length === 0 ? "no-match" : "ambiguous"
969
+ };
970
+ }
971
+ /**
972
+ * This function puts a value in an envelope with a type. The value does not change.
973
+ *
974
+ * @param value - The value.
975
+ * @param kind - The name of the kind.
976
+ * @returns The envelope.
977
+ */
978
+ function tag(value, kind) {
979
+ return typed(kind, value);
980
+ }
981
+ //#endregion
982
+ //#region src/parts.ts
983
+ var Bad = class {
984
+ reason;
985
+ at;
986
+ constructor(reason, at) {
987
+ this.reason = reason;
988
+ this.at = at;
989
+ }
990
+ };
991
+ function setOwn$1(target, key, value) {
992
+ Object.defineProperty(target, key, {
993
+ value,
994
+ enumerable: true,
995
+ writable: true,
996
+ configurable: true
997
+ });
998
+ }
999
+ function hasSlots(template) {
1000
+ return template.t !== "data";
1001
+ }
1002
+ /**
1003
+ * This function splits a value into its parts, by the shape that its kind declares. The evaluator, the view and
1004
+ * the static kit use the same split, thus each one sees the same parts.
1005
+ *
1006
+ * @param shape - The shape of the value. The default is data.
1007
+ * @param value - The value of the document.
1008
+ * @returns The parts and a way to fill their places, or the reason why the value does not have the shape.
1009
+ */
1010
+ function splitParts(shape, value) {
1011
+ const parts = [];
1012
+ const slot = (item, path, of) => {
1013
+ parts.push({
1014
+ path,
1015
+ key: path.join("."),
1016
+ item,
1017
+ ...of === void 0 ? {} : { of }
1018
+ });
1019
+ return {
1020
+ t: "slot",
1021
+ index: parts.length - 1
1022
+ };
1023
+ };
1024
+ const deep = (item, path, of) => {
1025
+ if (isEnvelope(item)) return slot(item, path, of);
1026
+ if (Array.isArray(item)) {
1027
+ const items = item.map((child, index) => deep(child, [...path, index], of));
1028
+ return items.some(hasSlots) ? {
1029
+ t: "list",
1030
+ items
1031
+ } : {
1032
+ t: "data",
1033
+ value: item
1034
+ };
1035
+ }
1036
+ if (isRecord(item)) {
1037
+ const fields = Object.keys(item).map((key) => [key, deep(item[key], [...path, key], of)]);
1038
+ return fields.some(([, child]) => hasSlots(child)) ? {
1039
+ t: "record",
1040
+ fields
1041
+ } : {
1042
+ t: "data",
1043
+ value: item
1044
+ };
1045
+ }
1046
+ return {
1047
+ t: "data",
1048
+ value: item
1049
+ };
1050
+ };
1051
+ const walk = (spec, item, path) => {
1052
+ if (spec === void 0 || spec.shape === "none") return {
1053
+ t: "data",
1054
+ value: item
1055
+ };
1056
+ if (spec.shape === "term") return slot(item, path, spec.of);
1057
+ if (spec.shape === "deep") return deep(item, path, spec.of);
1058
+ if (spec.shape === "list") {
1059
+ if (!Array.isArray(item)) throw new Bad("The value must be a list.", path);
1060
+ const each = spec.item ?? {
1061
+ shape: "term",
1062
+ ...spec.of === void 0 ? {} : { of: spec.of }
1063
+ };
1064
+ return {
1065
+ t: "list",
1066
+ items: item.map((child, index) => walk(each, child, [...path, index]))
1067
+ };
1068
+ }
1069
+ if (!isRecord(item)) throw new Bad("The value must be a record.", path);
1070
+ if (spec.shape === "map") {
1071
+ const each = spec.item ?? {
1072
+ shape: "term",
1073
+ ...spec.of === void 0 ? {} : { of: spec.of }
1074
+ };
1075
+ return {
1076
+ t: "record",
1077
+ fields: Object.keys(item).map((key) => [key, walk(each, item[key], [...path, key])])
1078
+ };
1079
+ }
1080
+ for (const name of spec.required ?? []) if (!Object.hasOwn(item, name)) throw new Bad(`The field ${name} is missing.`, path);
1081
+ const named = Array.isArray(spec.fields) ? Object.fromEntries(spec.fields.map((name) => [name, {
1082
+ shape: "term",
1083
+ ...spec.of === void 0 ? {} : { of: spec.of }
1084
+ }])) : spec.fields;
1085
+ const fields = [];
1086
+ for (const key of Object.keys(item)) {
1087
+ const fieldShape = Object.hasOwn(named, key) ? named[key] : spec.rest;
1088
+ fields.push([key, walk(fieldShape, item[key], [...path, key])]);
1089
+ }
1090
+ return {
1091
+ t: "record",
1092
+ fields
1093
+ };
1094
+ };
1095
+ let template;
1096
+ try {
1097
+ template = walk(shape, value, []);
1098
+ } catch (error) {
1099
+ if (error instanceof Bad) return {
1100
+ ok: false,
1101
+ reason: error.reason,
1102
+ at: error.at
1103
+ };
1104
+ throw error;
1105
+ }
1106
+ const strip = (node) => {
1107
+ if (node.t === "data") return node.value;
1108
+ if (node.t === "slot") return void 0;
1109
+ if (node.t === "list") return node.items.every((child) => child.t === "slot") ? null : node.items.map((child) => strip(child) ?? null);
1110
+ const out = {};
1111
+ for (const [key, child] of node.fields) {
1112
+ const kept = strip(child);
1113
+ if (kept !== void 0) setOwn$1(out, key, kept);
1114
+ }
1115
+ return out;
1116
+ };
1117
+ const fillWith = (node, handles) => {
1118
+ if (node.t === "data") return node.value;
1119
+ if (node.t === "slot") return handles[node.index];
1120
+ if (node.t === "list") return node.items.map((child) => fillWith(child, handles));
1121
+ const out = {};
1122
+ for (const [key, child] of node.fields) setOwn$1(out, key, fillWith(child, handles));
1123
+ return out;
1124
+ };
1125
+ return {
1126
+ ok: true,
1127
+ parts,
1128
+ data: strip(template) ?? null,
1129
+ fill: (handles) => fillWith(template, handles)
1130
+ };
1131
+ }
1132
+ /**
1133
+ * This function tells if the path of a part matches a pattern of a scope rule. The key `"*"` of a pattern matches
1134
+ * each key.
1135
+ *
1136
+ * @param path - The path of the part.
1137
+ * @param pattern - The pattern.
1138
+ * @returns `true` when the path matches.
1139
+ */
1140
+ function pathMatches(path, pattern) {
1141
+ if (path.length !== pattern.length) return false;
1142
+ for (let index = 0; index < path.length; index += 1) {
1143
+ const want = pattern[index];
1144
+ if (want !== "*" && String(want) !== String(path[index])) return false;
1145
+ }
1146
+ return true;
1147
+ }
1148
+ //#endregion
1149
+ //#region src/evaluate.ts
1150
+ /**
1151
+ * This function makes a context with names and capabilities. The other roots are empty, and an evaluation sets
1152
+ * the world.
1153
+ *
1154
+ * @param names - The names and their values.
1155
+ * @param caps - The effects that the term can cause.
1156
+ * @param self - The node of the term, if it has one.
1157
+ * @returns The context.
1158
+ */
1159
+ function makeContext(names = {}, caps = [], self = null) {
1160
+ return context({
1161
+ names,
1162
+ caps,
1163
+ self
1164
+ });
1165
+ }
1166
+ /**
1167
+ * This function gives the keys of a capability or of a kind of effect: a text is split at its dots.
1168
+ *
1169
+ * @param kind - The text or the keys.
1170
+ * @returns The keys, as texts.
1171
+ */
1172
+ function capKeys(kind) {
1173
+ return typeof kind === "string" ? kind.split(".") : kind.map(String);
1174
+ }
1175
+ /**
1176
+ * This function tells if the capabilities permit an effect. A capability permits the kind of its name, and each kind
1177
+ * under it: `store` permits `store.update`. The comparison is key by key, thus `fs.read` does not permit the
1178
+ * kind with the keys `fs` and `read.json`.
1179
+ *
1180
+ * @param caps - The capabilities of the context.
1181
+ * @param kind - The kind of the effect: a text with dots, or its keys.
1182
+ * @returns `true` when one capability permits it.
1183
+ */
1184
+ function permits(caps, kind) {
1185
+ const want = capKeys(kind);
1186
+ return caps.some((cap) => {
1187
+ const have = capKeys(cap);
1188
+ return have.length <= want.length && have.every((key, index) => key === want[index]);
1189
+ });
1190
+ }
1191
+ /**
1192
+ * This function narrows a context for a part: the part keeps only the capabilities that the context of its parent
1193
+ * permits. Thus an evaluator cannot give a part more effects than it has.
1194
+ *
1195
+ * @param parent - The context of the parent.
1196
+ * @param next - The context that the evaluator gives the part.
1197
+ * @returns The context of the part.
1198
+ */
1199
+ function narrow(parent, next) {
1200
+ if (next === parent || next.caps === parent.caps) return next;
1201
+ const caps = next.caps.filter((cap) => permits(parent.caps, cap));
1202
+ return caps.length === next.caps.length ? next : {
1203
+ ...next,
1204
+ caps
1205
+ };
1206
+ }
1207
+ function selfLabel(self) {
1208
+ try {
1209
+ if (isHostThing(self)) return self.label;
1210
+ if (self === null || self === void 0) return "none";
1211
+ return (JSON.stringify(self) ?? describe(self)).slice(0, 40);
1212
+ } catch {
1213
+ return describe(self).slice(0, 40);
1214
+ }
1215
+ }
1216
+ const NO_ANSWER = {
1217
+ verb: "eval",
1218
+ error: "no-answer",
1219
+ steps: []
1220
+ };
1221
+ /** This class holds the state of one evaluation or run: the machine, the effects, the clock and the trace. */
1222
+ var Session = class {
1223
+ machine;
1224
+ running;
1225
+ traced;
1226
+ perform;
1227
+ signal;
1228
+ effects;
1229
+ tick = 0;
1230
+ nextId = 0;
1231
+ current;
1232
+ holder;
1233
+ constructor(machine, running, traced, perform, signal, effects) {
1234
+ this.machine = machine;
1235
+ this.running = running;
1236
+ this.traced = traced;
1237
+ this.perform = perform;
1238
+ this.signal = signal;
1239
+ this.effects = effects ?? [];
1240
+ this.holder = traced ? holderNode() : void 0;
1241
+ this.current = this.holder;
1242
+ }
1243
+ resolve(ctx, path, options) {
1244
+ return resolve(this.machine, ctx, path, { hops: options?.hops ?? this.traced });
1245
+ }
1246
+ permitted(ctx, keys) {
1247
+ return this.machine.permits === void 0 ? permits(ctx.caps, keys) : this.machine.permits(ctx.caps, keys, ctx);
1248
+ }
1249
+ record(ctx, kind, payload, place) {
1250
+ const keys = typeof kind === "string" ? kind.split(".") : kind;
1251
+ const text = keys.join(".");
1252
+ if (!this.permitted(ctx, keys)) return fail("#CAP!", `The context has no capability for the effect ${text}.`, { place });
1253
+ const node = this.current;
1254
+ const effect = {
1255
+ index: this.effects.length,
1256
+ kind: text,
1257
+ path: keys,
1258
+ payload,
1259
+ node: node?.id ?? -1,
1260
+ at: this.tick,
1261
+ place,
1262
+ context: ctx
1263
+ };
1264
+ this.effects.push(effect);
1265
+ if (node !== void 0) node.effects.push(effect.index);
1266
+ return effect;
1267
+ }
1268
+ effect(ctx, kind, payload, place) {
1269
+ const text = typeof kind === "string" ? kind : kind.join(".");
1270
+ if (this.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
1271
+ const made = this.record(ctx, kind, payload, place);
1272
+ return "ok" in made ? made : ok(payload);
1273
+ }
1274
+ *runEffect(ctx, kind, payload, place) {
1275
+ const made = this.record(ctx, kind, payload, place);
1276
+ if ("ok" in made) return made;
1277
+ const perform = this.perform;
1278
+ if (!this.running || perform === void 0) return ok(payload);
1279
+ const failed = (error) => fail("#CALC!", `The host failed the effect ${made.kind}: ${describe(error)}`, {
1280
+ place,
1281
+ cause: error
1282
+ });
1283
+ let done;
1284
+ try {
1285
+ done = perform(made);
1286
+ } catch (error) {
1287
+ return failed(error);
1288
+ }
1289
+ if (!(done instanceof Promise)) return ok(done);
1290
+ return yield done.then((value) => ok(value), failed);
1291
+ }
1292
+ *all(list, options = {}) {
1293
+ const results = new Array(list.length);
1294
+ const limit = Math.max(1, options.concurrency ?? list.length);
1295
+ const active = [];
1296
+ let next = 0;
1297
+ const advance = (index, steps, input) => {
1298
+ let step;
1299
+ try {
1300
+ step = input.threw ? steps.throw(input.error) : steps.next(input.value);
1301
+ } catch (error) {
1302
+ results[index] = thrown(error, "a part");
1303
+ return;
1304
+ }
1305
+ if (step.done === true) {
1306
+ results[index] = step.value;
1307
+ return;
1308
+ }
1309
+ const item = {
1310
+ index,
1311
+ steps,
1312
+ pending: Promise.resolve()
1313
+ };
1314
+ item.pending = step.value.then((value) => ({
1315
+ item,
1316
+ value,
1317
+ threw: false
1318
+ }), (error) => ({
1319
+ item,
1320
+ error,
1321
+ threw: true
1322
+ }));
1323
+ active.push(item);
1324
+ };
1325
+ const start = () => {
1326
+ while (active.length < limit && next < list.length) {
1327
+ const index = next;
1328
+ next += 1;
1329
+ const begin = list[index];
1330
+ let steps;
1331
+ try {
1332
+ steps = begin();
1333
+ } catch (error) {
1334
+ results[index] = thrown(error, "a part");
1335
+ continue;
1336
+ }
1337
+ advance(index, steps, { threw: false });
1338
+ }
1339
+ };
1340
+ start();
1341
+ while (active.length > 0) {
1342
+ const settled = yield Promise.race(active.map((item) => item.pending));
1343
+ active.splice(active.indexOf(settled.item), 1);
1344
+ advance(settled.item.index, settled.item.steps, settled);
1345
+ start();
1346
+ }
1347
+ return results;
1348
+ }
1349
+ enter(meta, ctx) {
1350
+ const node = {
1351
+ id: this.nextId,
1352
+ path: meta.path,
1353
+ doc: meta.doc,
1354
+ declared: meta.declared,
1355
+ answer: meta.answer,
1356
+ side: meta.side,
1357
+ names: Object.keys(ctx.names),
1358
+ scope: ctx.names,
1359
+ caps: ctx.caps,
1360
+ self: selfLabel(ctx.self),
1361
+ enter: this.tick,
1362
+ exit: this.tick,
1363
+ result: fail("#NOEVAL", "The evaluation did not end."),
1364
+ children: [],
1365
+ effects: []
1366
+ };
1367
+ this.nextId += 1;
1368
+ this.tick += 1;
1369
+ (this.current ?? this.holder).children.push(node);
1370
+ return node;
1371
+ }
1372
+ exit(node, result) {
1373
+ node.result = result;
1374
+ node.exit = this.tick;
1375
+ this.tick += 1;
1376
+ }
1377
+ };
1378
+ function holderNode() {
1379
+ return {
1380
+ id: -1,
1381
+ path: [],
1382
+ doc: {
1383
+ type: "?",
1384
+ value: null
1385
+ },
1386
+ declared: false,
1387
+ answer: NO_ANSWER,
1388
+ side: "derive",
1389
+ names: [],
1390
+ scope: {},
1391
+ caps: [],
1392
+ self: "none",
1393
+ enter: 0,
1394
+ exit: 0,
1395
+ result: ok(null),
1396
+ children: [],
1397
+ effects: []
1398
+ };
1399
+ }
1400
+ /** This function drives steps that cannot wait. */
1401
+ function drive(steps) {
1402
+ const step = steps.next();
1403
+ if (step.done === true) return step.value;
1404
+ step.value.catch(() => void 0);
1405
+ steps.return(fail("#CALC!", "A part waited, and its parent cannot wait."));
1406
+ return fail("#CALC!", "A part waited, and its parent cannot wait.");
1407
+ }
1408
+ const ABORTED = Symbol("aborted");
1409
+ /** This function drives steps that can wait. The steps before the first wait happen at once, in the call. */
1410
+ async function driveWaiting(steps, signal) {
1411
+ const abort = () => {
1412
+ const reason = fail("#ABORT", `The host stopped the run: ${describe(signal?.reason ?? "no reason")}`, { cause: signal?.reason });
1413
+ try {
1414
+ steps.return(reason);
1415
+ } catch {}
1416
+ return reason;
1417
+ };
1418
+ const aborted = signal === void 0 ? void 0 : new Promise((resolve) => {
1419
+ if (signal.aborted) resolve(ABORTED);
1420
+ else signal.addEventListener("abort", () => resolve(ABORTED), { once: true });
1421
+ });
1422
+ if (signal?.aborted === true) return abort();
1423
+ let step = steps.next();
1424
+ while (step.done !== true) {
1425
+ let value;
1426
+ try {
1427
+ value = aborted === void 0 ? await step.value : await Promise.race([step.value, aborted]);
1428
+ } catch (error) {
1429
+ step = steps.throw(error);
1430
+ continue;
1431
+ }
1432
+ if (value === ABORTED) return abort();
1433
+ step = steps.next(value);
1434
+ }
1435
+ return step.value;
1436
+ }
1437
+ function isCompiledEvaluator(evaluator) {
1438
+ return typeof evaluator === "object" && evaluator !== null && typeof evaluator.compile === "function";
1439
+ }
1440
+ function isCompiledRunner(runner) {
1441
+ return typeof runner === "object" && runner !== null && typeof runner.compileRun === "function";
1442
+ }
1443
+ function failing(c, meta, place, code, reason) {
1444
+ const result = fail(code, reason, { place });
1445
+ const plain = () => result;
1446
+ return finish(c, meta, plain, function* () {
1447
+ return result;
1448
+ });
1449
+ }
1450
+ function finish(c, meta, code, run) {
1451
+ if (!c.traced) return {
1452
+ code,
1453
+ run
1454
+ };
1455
+ const tracedCode = (ctx, rt) => {
1456
+ const s = rt;
1457
+ const node = s.enter(meta, ctx);
1458
+ const before = s.current;
1459
+ s.current = node;
1460
+ let result;
1461
+ try {
1462
+ result = code(ctx, rt);
1463
+ } finally {
1464
+ s.current = before;
1465
+ }
1466
+ s.exit(node, result);
1467
+ return result;
1468
+ };
1469
+ const tracedRun = function* (ctx, rt) {
1470
+ const s = rt;
1471
+ const node = s.enter(meta, ctx);
1472
+ const steps = run(ctx, rt);
1473
+ let input;
1474
+ let threw = false;
1475
+ for (;;) {
1476
+ const before = s.current;
1477
+ s.current = node;
1478
+ let step;
1479
+ try {
1480
+ step = threw ? steps.throw(input) : steps.next(input);
1481
+ } finally {
1482
+ s.current = before;
1483
+ }
1484
+ if (step.done === true) {
1485
+ s.exit(node, step.value);
1486
+ return step.value;
1487
+ }
1488
+ try {
1489
+ input = yield step.value;
1490
+ threw = false;
1491
+ } catch (error) {
1492
+ input = error;
1493
+ threw = true;
1494
+ }
1495
+ }
1496
+ };
1497
+ return {
1498
+ code: tracedCode,
1499
+ run: tracedRun
1500
+ };
1501
+ }
1502
+ function compileNode(c, value, place, declared, depth) {
1503
+ const envelope = isEnvelope(value) ? value : void 0;
1504
+ const doc = envelope ?? {
1505
+ type: declared ?? "?",
1506
+ value
1507
+ };
1508
+ const meta0 = {
1509
+ path: place.map(String),
1510
+ doc,
1511
+ declared: envelope === void 0,
1512
+ answer: NO_ANSWER,
1513
+ side: "derive"
1514
+ };
1515
+ if (envelope === void 0 && declared === void 0) return failing(c, meta0, place, "#PART?", "The value has no envelope, and its position declares no type.");
1516
+ if (depth > c.maxDepth) return failing(c, meta0, place, "#DEPTH!", `The nesting is deeper than ${c.maxDepth} documents.`);
1517
+ const answer = ask(c.machine.world, envelope ?? {}, "eval", declared === void 0 ? {} : { declaredType: declared });
1518
+ const found = lookupKind(c.machine.world, doc.type);
1519
+ const kind = found.ok ? found.kind : void 0;
1520
+ const meta = {
1521
+ ...meta0,
1522
+ answer,
1523
+ side: kind?.side ?? "derive"
1524
+ };
1525
+ const kindText = typeName(doc.type);
1526
+ if (answer.error === "unknown-kind") return failing(c, meta, place, "#KIND?", `The world has no kind ${kindText}.`);
1527
+ const name = answer.evaluator;
1528
+ const runners = c.machine.runners ?? {};
1529
+ const runner = name !== void 0 && Object.hasOwn(runners, name) ? runners[name] : void 0;
1530
+ const evaluator = name !== void 0 && Object.hasOwn(c.machine.evaluators, name) ? c.machine.evaluators[name] : void 0;
1531
+ if (name === void 0 || runner === void 0 && evaluator === void 0) return failing(c, meta, place, "#NOEVAL", `No rung gives an evaluator for ${kindText}.`);
1532
+ const split = splitParts(kind?.parts, doc.value);
1533
+ if (!split.ok) {
1534
+ const at = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
1535
+ return failing(c, meta, place, "#VALUE!", `${split.reason}${at} The kind is ${kindText}.`);
1536
+ }
1537
+ const parts = split.parts.map((slot) => {
1538
+ const compiled = compileNode(c, slot.item, [...place, ...slot.path], slot.of, depth + 1);
1539
+ const header = isEnvelope(slot.item) && isRecord(slot.item) && typeof slot.item["name"] === "string" ? slot.item["name"] : void 0;
1540
+ return {
1541
+ key: slot.key,
1542
+ path: slot.path,
1543
+ ...header === void 0 ? {} : { name: header },
1544
+ code: compiled.code,
1545
+ run: compiled.run
1546
+ };
1547
+ });
1548
+ const input = {
1549
+ kind: kindText,
1550
+ typePath: found.ok ? found.path : [],
1551
+ doc,
1552
+ value: split.parts.length === 0 ? doc.value : split.fill(parts),
1553
+ data: split.data,
1554
+ parts,
1555
+ place,
1556
+ machine: c.machine
1557
+ };
1558
+ const who = `the evaluator ${name}`;
1559
+ if (runner !== void 0) {
1560
+ const inner = isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill);
1561
+ const run = function* (ctx, rt) {
1562
+ try {
1563
+ return placed(yield* inner(ctx, rt), place);
1564
+ } catch (error) {
1565
+ return placed(thrown(error, `the runner ${name}`), place);
1566
+ }
1567
+ };
1568
+ 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));
1569
+ return finish(c, meta, code, run);
1570
+ }
1571
+ const inner = isCompiledEvaluator(evaluator) ? evaluator.compile(input) : interpret(evaluator, input, split.fill);
1572
+ const code = (ctx, rt) => {
1573
+ try {
1574
+ return placed(inner(ctx, rt), place);
1575
+ } catch (error) {
1576
+ return placed(thrown(error, who), place);
1577
+ }
1578
+ };
1579
+ return finish(c, meta, code, function* (ctx, rt) {
1580
+ return code(ctx, rt);
1581
+ });
1582
+ }
1583
+ function interpret(fn, input, fill) {
1584
+ const { kind, typePath, doc, data, place } = input;
1585
+ const compiled = input.parts;
1586
+ const hasParts = compiled.length > 0;
1587
+ return (ctx, rt) => {
1588
+ const parts = compiled.map((part) => ({
1589
+ key: part.key,
1590
+ path: part.path,
1591
+ ...part.name === void 0 ? {} : { name: part.name },
1592
+ evaluate: (override) => part.code(override === void 0 ? ctx : narrow(ctx, override), rt)
1593
+ }));
1594
+ return fn({
1595
+ kind,
1596
+ typePath,
1597
+ doc,
1598
+ value: hasParts ? fill(parts) : doc.value,
1599
+ data,
1600
+ parts,
1601
+ context: ctx,
1602
+ place,
1603
+ resolve: (path, options) => rt.resolve(ctx, path, options),
1604
+ effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place)
1605
+ });
1606
+ };
1607
+ }
1608
+ function interpretRun(fn, input, fill) {
1609
+ const { kind, typePath, doc, data, place } = input;
1610
+ const compiled = input.parts;
1611
+ const hasParts = compiled.length > 0;
1612
+ return (ctx, rt) => {
1613
+ const parts = compiled.map((part) => ({
1614
+ key: part.key,
1615
+ path: part.path,
1616
+ ...part.name === void 0 ? {} : { name: part.name },
1617
+ evaluate: (override) => part.run(override === void 0 ? ctx : narrow(ctx, override), rt)
1618
+ }));
1619
+ return fn({
1620
+ kind,
1621
+ typePath,
1622
+ doc,
1623
+ value: hasParts ? fill(parts) : doc.value,
1624
+ data,
1625
+ parts,
1626
+ context: ctx,
1627
+ place,
1628
+ resolve: (path, options) => rt.resolve(ctx, path, options),
1629
+ effect: (effectKind, payload) => rt.runEffect(ctx, effectKind, payload, place),
1630
+ all: (list, options) => rt.all(list.map((part) => () => part.evaluate()), options)
1631
+ });
1632
+ };
1633
+ }
1634
+ function rootContext(machine, ctx) {
1635
+ return ctx.world === null ? {
1636
+ ...ctx,
1637
+ world: worldThing(machine.world)
1638
+ } : ctx;
1639
+ }
1640
+ /**
1641
+ * This function compiles a document into a plan. The ladder, the kinds and the parts are known after it. A plan
1642
+ * evaluates many times. A document that does not compile gives a plan that fails with the reason, when it evaluates.
1643
+ *
1644
+ * @param machine - The world and the code of the evaluators.
1645
+ * @param doc - The document, as JSON.
1646
+ * @param options - The type of a bare root value, and a trace.
1647
+ * @returns The plan.
1648
+ */
1649
+ function prepare(machine, doc, options = {}) {
1650
+ const c = {
1651
+ machine,
1652
+ traced: options.trace === true,
1653
+ maxDepth: machine.maxDepth ?? 1e3
1654
+ };
1655
+ let root;
1656
+ try {
1657
+ root = compileNode(c, doc, [], options.declaredType, 0);
1658
+ } catch (error) {
1659
+ const result = thrown(error, "the compilation");
1660
+ root = {
1661
+ code: () => result,
1662
+ run: function* () {
1663
+ return result;
1664
+ }
1665
+ };
1666
+ }
1667
+ const compiled = {
1668
+ key: "",
1669
+ path: [],
1670
+ code: root.code,
1671
+ run: root.run
1672
+ };
1673
+ return {
1674
+ machine,
1675
+ traced: c.traced,
1676
+ root: compiled,
1677
+ evaluate(context = makeContext(), evalOptions = {}) {
1678
+ const s = new Session(machine, false, c.traced, void 0, void 0, evalOptions.effects);
1679
+ return root.code(rootContext(machine, context), s);
1680
+ },
1681
+ run(context = makeContext(), runOptions = {}) {
1682
+ const s = new Session(machine, true, c.traced, runOptions.perform, runOptions.signal, void 0);
1683
+ return driveWaiting(root.run(rootContext(machine, context), s), runOptions.signal);
1684
+ }
1685
+ };
1686
+ }
1687
+ /**
1688
+ * This function makes a cache of plans for one machine. A document object compiles one time. Use it only for
1689
+ * documents that do not change after they are made.
1690
+ *
1691
+ * @param machine - The machine.
1692
+ * @param options - The options of each compilation.
1693
+ * @returns A function that gives the plan of a document.
1694
+ */
1695
+ function planner(machine, options = {}) {
1696
+ const objects = /* @__PURE__ */ new WeakMap();
1697
+ const values = /* @__PURE__ */ new Map();
1698
+ return (doc) => {
1699
+ if (typeof doc === "object" && doc !== null) {
1700
+ let plan = objects.get(doc);
1701
+ if (plan === void 0) {
1702
+ plan = prepare(machine, doc, options);
1703
+ objects.set(doc, plan);
1704
+ }
1705
+ return plan;
1706
+ }
1707
+ let plan = values.get(doc);
1708
+ if (plan === void 0) {
1709
+ plan = prepare(machine, doc, options);
1710
+ if (values.size > 1e3) values.clear();
1711
+ values.set(doc, plan);
1712
+ }
1713
+ return plan;
1714
+ };
1715
+ }
1716
+ function record(s, result) {
1717
+ const root = s.holder?.children[0];
1718
+ if (root === void 0) throw new Error("The evaluation made no trace.");
1719
+ return {
1720
+ result,
1721
+ trace: root,
1722
+ effects: s.effects,
1723
+ time: s.tick
1724
+ };
1725
+ }
1726
+ /**
1727
+ * This function evaluates a document, and records each evaluation in a trace.
1728
+ *
1729
+ * The verb `eval` goes up the ladder for each document. The kind of the document tells where its parts are. The
1730
+ * evaluator gets each part sealed: it can evaluate the part, and it cannot read its value. A failure gives an error
1731
+ * value, and the function does not throw. An effect is recorded in its order, and nothing does it.
1732
+ *
1733
+ * @param machine - The world and the code of the evaluators.
1734
+ * @param doc - The document, as JSON.
1735
+ * @param context - The names and the capabilities at the root.
1736
+ * @param declaredType - The type of a bare root value.
1737
+ * @returns The result, the trace and the effects in their order.
1738
+ */
1739
+ function evaluate(machine, doc, context = makeContext(), declaredType) {
1740
+ const plan = prepare(machine, doc, {
1741
+ trace: true,
1742
+ ...declaredType === void 0 ? {} : { declaredType }
1743
+ });
1744
+ const s = new Session(machine, false, true, void 0, void 0, void 0);
1745
+ return record(s, plan.root.code(rootContext(machine, context), s));
1746
+ }
1747
+ /**
1748
+ * This function runs a document: an evaluation in which the host does each effect. A runner waits for its
1749
+ * parts and for its effects, thus a step can read the result of the step before. The part of the run before the
1750
+ * first wait happens at once, in the call. The run records a trace.
1751
+ *
1752
+ * @param machine - The world, the evaluators and the runners.
1753
+ * @param doc - The document, as JSON.
1754
+ * @param context - The names and the capabilities at the root.
1755
+ * @param options - The host code that does the effects, the type of a bare root value, and a signal.
1756
+ * @returns The result, the trace and the effects in their order, when the run ends.
1757
+ */
1758
+ async function run(machine, doc, context = makeContext(), options = {}) {
1759
+ const plan = prepare(machine, doc, {
1760
+ trace: true,
1761
+ ...options.declaredType === void 0 ? {} : { declaredType: options.declaredType }
1762
+ });
1763
+ const s = new Session(machine, true, true, options.perform, options.signal, void 0);
1764
+ return record(s, await driveWaiting(plan.root.run(rootContext(machine, context), s), options.signal));
1765
+ }
1766
+ /**
1767
+ * This function finds a node of a trace by its path.
1768
+ *
1769
+ * @param trace - The root of the trace.
1770
+ * @param path - The keys from the root.
1771
+ * @returns The first evaluation at that path, or `undefined` when the document did not evaluate.
1772
+ */
1773
+ function nodeAt(trace, path) {
1774
+ const want = path.map(String);
1775
+ let node = trace;
1776
+ let at = 0;
1777
+ while (node !== void 0 && at < want.length) {
1778
+ const here = node;
1779
+ node = here.children.find((child) => {
1780
+ const tail = child.path.slice(here.path.length);
1781
+ return tail.length > 0 && tail.every((key, index) => key === want[at + index]);
1782
+ });
1783
+ if (node !== void 0) at += node.path.length - here.path.length;
1784
+ }
1785
+ return node;
1786
+ }
1787
+ /**
1788
+ * This function lists the nodes of a trace in the order of their start.
1789
+ *
1790
+ * @param trace - The root of the trace.
1791
+ * @returns Each node, the root first.
1792
+ */
1793
+ function flatten(trace) {
1794
+ const out = [];
1795
+ const stack = [trace];
1796
+ while (stack.length > 0) {
1797
+ const node = stack.pop();
1798
+ out.push(node);
1799
+ for (let index = node.children.length - 1; index >= 0; index -= 1) stack.push(node.children[index]);
1800
+ }
1801
+ return out.sort((a, b) => a.enter - b.enter);
1802
+ }
1803
+ /**
1804
+ * This function lists the errors of a trace: each node whose result is an error and whose parts did not fail
1805
+ * the same way. These nodes are where each error started.
1806
+ *
1807
+ * @param trace - The root of the trace.
1808
+ * @returns The errors, in the order of their start.
1809
+ */
1810
+ function failures(trace) {
1811
+ 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);
1812
+ }
1813
+ //#endregion
1814
+ //#region src/stdlib.ts
1815
+ /**
1816
+ * The world of the examples: small kinds for numbers, lists, conditions, names, sequences and effects.
1817
+ * Each kind names an evaluator for `eval` and a view for `view`. The site draws each document with its view.
1818
+ */
1819
+ const STD_WORLD = {
1820
+ kinds: {
1821
+ number: {
1822
+ verbs: {
1823
+ eval: "data.number",
1824
+ view: "view.value"
1825
+ },
1826
+ summary: "A number. Its value is the result."
1827
+ },
1828
+ text: {
1829
+ verbs: {
1830
+ eval: "data.text",
1831
+ view: "view.value"
1832
+ },
1833
+ summary: "A text. Its value is the result."
1834
+ },
1835
+ bool: {
1836
+ verbs: {
1837
+ eval: "data.bool",
1838
+ view: "view.value"
1839
+ },
1840
+ summary: "A truth value. Its value is the result."
1841
+ },
1842
+ add: {
1843
+ verbs: {
1844
+ eval: "math.add",
1845
+ view: "view.op"
1846
+ },
1847
+ parts: {
1848
+ shape: "list",
1849
+ of: "number"
1850
+ },
1851
+ summary: "The sum of its parts. A bare number in its list gets the type number."
1852
+ },
1853
+ multiply: {
1854
+ verbs: {
1855
+ eval: "math.multiply",
1856
+ view: "view.op"
1857
+ },
1858
+ parts: {
1859
+ shape: "list",
1860
+ of: "number"
1861
+ },
1862
+ summary: "The product of its parts."
1863
+ },
1864
+ greater: {
1865
+ verbs: {
1866
+ eval: "math.greater",
1867
+ view: "view.op"
1868
+ },
1869
+ parts: {
1870
+ shape: "list",
1871
+ of: "number"
1872
+ },
1873
+ summary: "True when the first part is greater than the second part."
1874
+ },
1875
+ list: {
1876
+ verbs: {
1877
+ eval: "list.all",
1878
+ view: "view.op"
1879
+ },
1880
+ parts: { shape: "list" },
1881
+ summary: "The results of its parts, as a list."
1882
+ },
1883
+ if: {
1884
+ verbs: {
1885
+ eval: "control.if",
1886
+ view: "view.op"
1887
+ },
1888
+ parts: {
1889
+ shape: "record",
1890
+ fields: [
1891
+ "test",
1892
+ "then",
1893
+ "else"
1894
+ ]
1895
+ },
1896
+ summary: "It evaluates the test, then only the branch that the test selects."
1897
+ },
1898
+ ref: {
1899
+ verbs: {
1900
+ eval: "ref.resolve",
1901
+ view: "view.ref"
1902
+ },
1903
+ summary: "A path from a root of the context. Each step goes up the ladder of the verb deref."
1904
+ },
1905
+ nearest: {
1906
+ verbs: {
1907
+ step: "walk.nearest",
1908
+ view: "view.card"
1909
+ },
1910
+ summary: "A step of a path: it follows a key again and again, until it reaches a node of a type."
1911
+ },
1912
+ panel: {
1913
+ verbs: {
1914
+ eval: "tree.node",
1915
+ view: "view.op"
1916
+ },
1917
+ parts: {
1918
+ shape: "record",
1919
+ fields: [
1920
+ "width",
1921
+ "height",
1922
+ "children"
1923
+ ],
1924
+ of: "number"
1925
+ },
1926
+ summary: "A node of a tree. Its parts see it as self, and each node links to its parent."
1927
+ },
1928
+ box: {
1929
+ verbs: {
1930
+ eval: "tree.node",
1931
+ view: "view.op"
1932
+ },
1933
+ parts: {
1934
+ shape: "record",
1935
+ fields: [
1936
+ "width",
1937
+ "height",
1938
+ "children"
1939
+ ],
1940
+ of: "number"
1941
+ },
1942
+ summary: "A node of a tree, as panel. A formula in a box can read the parent of the box."
1943
+ },
1944
+ math: { kinds: {
1945
+ sum: {
1946
+ type: "ref",
1947
+ value: ["world", "add"]
1948
+ },
1949
+ product: {
1950
+ type: "ref",
1951
+ value: ["world", "multiply"]
1952
+ }
1953
+ } },
1954
+ seq: {
1955
+ verbs: {
1956
+ eval: "control.seq",
1957
+ view: "view.seq"
1958
+ },
1959
+ parts: { shape: "list" },
1960
+ scope: [{
1961
+ names: { headers: true },
1962
+ in: [["*"]],
1963
+ sequential: true
1964
+ }, {
1965
+ names: { fixed: ["last"] },
1966
+ in: [["*"]],
1967
+ sequential: true
1968
+ }],
1969
+ summary: "Its steps, in order. The result of a named step is a name for the later steps."
1970
+ },
1971
+ pure: {
1972
+ verbs: {
1973
+ eval: "control.pure",
1974
+ view: "view.op"
1975
+ },
1976
+ parts: { shape: "list" },
1977
+ summary: "It evaluates its part without capabilities, thus an effect in it fails."
1978
+ },
1979
+ log: {
1980
+ verbs: {
1981
+ eval: "effect.log",
1982
+ view: "view.op"
1983
+ },
1984
+ parts: {
1985
+ shape: "list",
1986
+ of: "text"
1987
+ },
1988
+ side: "run",
1989
+ summary: "It writes the result of its part to the log. This is an effect."
1990
+ },
1991
+ write: {
1992
+ verbs: {
1993
+ eval: "effect.write",
1994
+ view: "view.op"
1995
+ },
1996
+ parts: {
1997
+ shape: "record",
1998
+ fields: ["value"]
1999
+ },
2000
+ side: "run",
2001
+ summary: "It writes the result of its part to the store, at its key. This is an effect."
2002
+ }
2003
+ },
2004
+ defaults: { view: "view.card" }
2005
+ };
2006
+ function isNumber(value) {
2007
+ return typeof value === "number" && Number.isFinite(value);
2008
+ }
2009
+ /** This function evaluates each part, collects the errors, and gives the values when no part failed. */
2010
+ function allValues(results) {
2011
+ const errors = results.filter((result) => !result.ok);
2012
+ const first = errors[0];
2013
+ if (first !== void 0 && errors.length === 1) return { error: first };
2014
+ if (errors.length > 1) return { error: fail("#ARGS", `${errors.length} parts failed.`) };
2015
+ return { values: results.map((result) => result.ok ? result.value : null) };
2016
+ }
2017
+ function numbers(values, kind) {
2018
+ const bad = values.findIndex((value) => !isNumber(value));
2019
+ if (bad !== -1) return fail("#VALUE!", `Part ${bad} of ${kind} is not a number.`);
2020
+ return values.filter(isNumber);
2021
+ }
2022
+ function dataOf(type) {
2023
+ return ({ data }) => typeof data === type && (type !== "number" || isNumber(data)) ? ok(data) : fail("#VALUE!", `The value must be a ${type === "string" ? "text" : type}.`);
2024
+ }
2025
+ /** The code of the evaluators of the standard world, by name. */
2026
+ const STD_EVALUATORS = {
2027
+ "data.number": dataOf("number"),
2028
+ "data.text": dataOf("string"),
2029
+ "data.bool": dataOf("boolean"),
2030
+ "math.add": ({ parts }) => {
2031
+ const all = allValues(parts.map((part) => part.evaluate()));
2032
+ if ("error" in all) return all.error;
2033
+ const values = numbers(all.values, "add");
2034
+ return Array.isArray(values) ? ok(values.reduce((sum, value) => sum + value, 0)) : values;
2035
+ },
2036
+ "math.multiply": ({ parts }) => {
2037
+ const all = allValues(parts.map((part) => part.evaluate()));
2038
+ if ("error" in all) return all.error;
2039
+ const values = numbers(all.values, "multiply");
2040
+ return Array.isArray(values) ? ok(values.reduce((product, value) => product * value, 1)) : values;
2041
+ },
2042
+ "math.greater": ({ parts }) => {
2043
+ if (parts.length !== 2) return fail("#VALUE!", "greater needs exactly two parts.");
2044
+ const all = allValues(parts.map((part) => part.evaluate()));
2045
+ if ("error" in all) return all.error;
2046
+ const values = numbers(all.values, "greater");
2047
+ if (!Array.isArray(values)) return values;
2048
+ const [a, b] = values;
2049
+ return ok((a ?? 0) > (b ?? 0));
2050
+ },
2051
+ "ref.resolve": ({ data, resolve }) => {
2052
+ 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\"].");
2053
+ const found = resolve(data);
2054
+ return found.ok ? ok(valueOfThing(found.thing)) : fail(found.code, found.reason);
2055
+ },
2056
+ "tree.node": (input) => treeNode(input.parts, input.context, input.kind)
2057
+ };
2058
+ /**
2059
+ * This function evaluates a node of a tree. The node is a host thing. A step `parent` reaches the node that holds
2060
+ * it. A step with the name of a part evaluates that part on demand, one time. Each part sees the node as self.
2061
+ */
2062
+ function treeNode(parts, context, type) {
2063
+ const memo = /* @__PURE__ */ new Map();
2064
+ const read = (key) => {
2065
+ const known = memo.get(key);
2066
+ if (known !== void 0) return known;
2067
+ const part = parts.find((item) => item.key === key);
2068
+ if (part === void 0) return void 0;
2069
+ const result = part.evaluate({
2070
+ ...context,
2071
+ self: me
2072
+ });
2073
+ memo.set(key, result);
2074
+ return result;
2075
+ };
2076
+ const me = {
2077
+ [THING]: true,
2078
+ type,
2079
+ label: `node ${type}`,
2080
+ deref(key) {
2081
+ if (key === "parent") return context.self;
2082
+ if (key === "type") return type;
2083
+ const result = read(String(key));
2084
+ return result?.ok === true ? result.value : void 0;
2085
+ },
2086
+ toJson() {
2087
+ const out = {};
2088
+ for (const part of parts) {
2089
+ const result = read(part.key);
2090
+ out[part.key] = result?.ok === true ? result.value : null;
2091
+ }
2092
+ return out;
2093
+ }
2094
+ };
2095
+ const out = {};
2096
+ for (const part of parts) {
2097
+ const result = read(part.key);
2098
+ if (result === void 0) continue;
2099
+ if (!result.ok) return result;
2100
+ out[part.key] = result.value;
2101
+ }
2102
+ return ok(out);
2103
+ }
2104
+ function hostType(node, wanted) {
2105
+ return isHostThing(node) && node.type === wanted;
2106
+ }
2107
+ /**
2108
+ * This function makes a walker `nearest`. The walker follows a key (the default is `parent`) until the node that it
2109
+ * reaches has the wanted type. With `self: true`, the node where the walk starts is the first candidate. A node
2110
+ * without that key ends the walk with an error.
2111
+ *
2112
+ * @param options - The test of a type and the most hops.
2113
+ * @returns The walker.
2114
+ */
2115
+ function nearestWalker(options = {}) {
2116
+ const matches = options.matches ?? hostType;
2117
+ const maxHops = options.maxHops ?? 256;
2118
+ return ({ data, here, deref }) => {
2119
+ const follow = isRecord(data) && typeof field(data, "follow") === "string" ? String(field(data, "follow")) : "parent";
2120
+ const wanted = isRecord(data) ? field(data, "type") : void 0;
2121
+ const fromSelf = isRecord(data) && field(data, "self") === true;
2122
+ let at = here;
2123
+ for (let hops = 0; hops < maxHops; hops += 1) {
2124
+ if (hops > 0 || !fromSelf) at = at === void 0 || at === null ? void 0 : deref(at, follow);
2125
+ if (at === void 0 || at === null) return {
2126
+ ok: false,
2127
+ reason: `No node of the type ${String(wanted)} is on the way.`
2128
+ };
2129
+ if (matches(at, wanted)) return {
2130
+ ok: true,
2131
+ thing: at
2132
+ };
2133
+ }
2134
+ return {
2135
+ ok: false,
2136
+ reason: "The walk is too long."
2137
+ };
2138
+ };
2139
+ }
2140
+ /** The walkers of the standard world, by name. */
2141
+ const STD_WALKERS = { "walk.nearest": nearestWalker() };
2142
+ /** This function evaluates parts one after the other, and stops at the first error. */
2143
+ function* inOrder(parts) {
2144
+ const values = [];
2145
+ for (const part of parts) {
2146
+ const result = yield* part.evaluate();
2147
+ if (!result.ok) return { error: result };
2148
+ values.push(result.value);
2149
+ }
2150
+ return { values };
2151
+ }
2152
+ /**
2153
+ * The runners of the standard world, by name: the kinds that can hold effects. Each one evaluates its parts in
2154
+ * order. In a run, an effect ends before the next step starts.
2155
+ */
2156
+ const STD_RUNNERS = {
2157
+ "control.seq": function* ({ parts, context }) {
2158
+ let names = { ...context.names };
2159
+ let last = null;
2160
+ for (const part of parts) {
2161
+ const result = yield* part.evaluate({
2162
+ ...context,
2163
+ names
2164
+ });
2165
+ if (!result.ok) return result;
2166
+ names = {
2167
+ ...names,
2168
+ last: result.value
2169
+ };
2170
+ if (part.name !== void 0) names = {
2171
+ ...names,
2172
+ [part.name]: result.value
2173
+ };
2174
+ last = result.value;
2175
+ }
2176
+ return ok(last);
2177
+ },
2178
+ "control.pure": function* ({ parts, context }) {
2179
+ const only = parts[0];
2180
+ if (only === void 0) return fail("#VALUE!", "pure needs one part.");
2181
+ return yield* only.evaluate({
2182
+ ...context,
2183
+ caps: []
2184
+ });
2185
+ },
2186
+ "control.if": function* ({ parts }) {
2187
+ const test = parts.find((part) => part.key === "test");
2188
+ if (test === void 0) return fail("#VALUE!", "if needs a test.");
2189
+ const chosen = yield* test.evaluate();
2190
+ if (!chosen.ok) return chosen;
2191
+ if (typeof chosen.value !== "boolean") return fail("#VALUE!", "The test of if must give a truth value.");
2192
+ const branch = parts.find((part) => part.key === (chosen.value === true ? "then" : "else"));
2193
+ return branch === void 0 ? ok(null) : yield* branch.evaluate();
2194
+ },
2195
+ "list.all": function* ({ parts }) {
2196
+ const all = yield* inOrder(parts);
2197
+ return "error" in all ? all.error : ok(all.values);
2198
+ },
2199
+ "effect.log": function* ({ parts, effect }) {
2200
+ const all = yield* inOrder(parts);
2201
+ if ("error" in all) return all.error;
2202
+ return yield* effect("log", all.values.length === 1 ? all.values[0] ?? null : all.values);
2203
+ },
2204
+ "effect.write": function* ({ parts, data, effect }) {
2205
+ const key = isRecord(data) ? field(data, "key") : void 0;
2206
+ if (typeof key !== "string") return fail("#VALUE!", "write needs a text key.");
2207
+ const value = parts.find((part) => part.key === "value");
2208
+ if (value === void 0) return fail("#VALUE!", "write needs a value.");
2209
+ const result = yield* value.evaluate();
2210
+ if (!result.ok) return result;
2211
+ return yield* effect("write", {
2212
+ key,
2213
+ value: result.value
2214
+ });
2215
+ }
2216
+ };
2217
+ /** The machine of the examples: the standard world, its evaluators, its runners and its walkers. */
2218
+ const STD_MACHINE = {
2219
+ world: STD_WORLD,
2220
+ evaluators: STD_EVALUATORS,
2221
+ runners: STD_RUNNERS,
2222
+ walkers: STD_WALKERS
2223
+ };
2224
+ /** The capabilities of the examples at the root: the two effects of the standard world. */
2225
+ const STD_CAPS = ["log", "write"];
2226
+ //#endregion
2227
+ //#region src/order.ts
2228
+ /**
2229
+ * This function finds the references that a sequence cannot resolve. A step can read only the steps before it.
2230
+ *
2231
+ * @param steps - The steps, in the written order.
2232
+ * @returns One problem for each bad reference, in the written order.
2233
+ */
2234
+ function checkSteps(steps) {
2235
+ const problems = [];
2236
+ steps.forEach((step, index) => {
2237
+ for (const name of step.reads) {
2238
+ const at = steps.findIndex((other) => other.id === name);
2239
+ if (at === -1) problems.push({
2240
+ step: step.id,
2241
+ reads: name,
2242
+ problem: "unknown-name"
2243
+ });
2244
+ else if (at >= index) problems.push({
2245
+ step: step.id,
2246
+ reads: name,
2247
+ problem: "read-before-defined"
2248
+ });
2249
+ }
2250
+ });
2251
+ return problems;
2252
+ }
2253
+ /**
2254
+ * This function gives the data edges: a step comes after each earlier step that it reads.
2255
+ *
2256
+ * @param steps - The steps, in the written order.
2257
+ * @returns The edges. A bad reference gives no edge.
2258
+ */
2259
+ function dataEdges(steps) {
2260
+ const edges = [];
2261
+ steps.forEach((step, index) => {
2262
+ for (const name of step.reads) {
2263
+ const at = steps.findIndex((other) => other.id === name);
2264
+ if (at !== -1 && at < index) edges.push([name, step.id]);
2265
+ }
2266
+ });
2267
+ return edges;
2268
+ }
2269
+ /**
2270
+ * This function gives the effect edges: each step with an effect comes after the previous step with an effect.
2271
+ * Effects keep their written order. Steps without an effect get no effect edge.
2272
+ *
2273
+ * @param steps - The steps, in the written order.
2274
+ * @returns The edges, one for each pair of consecutive effect steps.
2275
+ */
2276
+ function effectEdges(steps) {
2277
+ const edges = [];
2278
+ let previous;
2279
+ for (const step of steps) {
2280
+ if (!step.effect) continue;
2281
+ if (previous !== void 0) edges.push([previous, step.id]);
2282
+ previous = step.id;
2283
+ }
2284
+ return edges;
2285
+ }
2286
+ /**
2287
+ * This function tells if an order of the steps gives the same meaning as the written order.
2288
+ * The order must contain each step one time, and it must obey the data edges and the effect edges.
2289
+ *
2290
+ * @param steps - The steps, in the written order.
2291
+ * @param order - The names of the steps in the order to examine.
2292
+ * @returns `true` when the order is permitted.
2293
+ */
2294
+ function isAllowedOrder(steps, order) {
2295
+ if (order.length !== steps.length) return false;
2296
+ if (new Set(order).size !== order.length) return false;
2297
+ if (!steps.every((step) => order.includes(step.id))) return false;
2298
+ const position = new Map(order.map((id, index) => [id, index]));
2299
+ return [...dataEdges(steps), ...effectEdges(steps)].every(([before, after]) => (position.get(before) ?? 0) < (position.get(after) ?? 0));
2300
+ }
2301
+ /**
2302
+ * This function puts the steps in layers. Each step goes one layer after the last step that it must follow.
2303
+ * The steps of one layer do not depend on each other, thus they can run at the same time.
2304
+ *
2305
+ * @param steps - The steps, in the written order.
2306
+ * @returns The layers, first to last. Each layer keeps the written order.
2307
+ */
2308
+ function layers(steps) {
2309
+ const edges = [...dataEdges(steps), ...effectEdges(steps)];
2310
+ const depth = /* @__PURE__ */ new Map();
2311
+ for (const step of steps) {
2312
+ const before = edges.filter(([, after]) => after === step.id).map(([first]) => depth.get(first) ?? 0);
2313
+ depth.set(step.id, before.length === 0 ? 0 : Math.max(...before) + 1);
2314
+ }
2315
+ const result = [];
2316
+ for (const step of steps) {
2317
+ const at = depth.get(step.id) ?? 0;
2318
+ while (result.length <= at) result.push([]);
2319
+ result[at]?.push(step.id);
2320
+ }
2321
+ return result;
2322
+ }
2323
+ //#endregion
2324
+ //#region src/context.ts
2325
+ /** The parts of a context. A term can reach outside itself only through these parts. */
2326
+ const PARTS = [
2327
+ "self",
2328
+ "names",
2329
+ "world",
2330
+ "event",
2331
+ "caps"
2332
+ ];
2333
+ /** The descriptions of the parts, in the order of `PARTS`. */
2334
+ const PART_INFO = [
2335
+ {
2336
+ part: "self",
2337
+ title: "Self",
2338
+ reaches: "A position in a structure: the node itself, its parent, its children or its siblings.",
2339
+ direction: "Any direction that a move permits: up, down or to the side."
2340
+ },
2341
+ {
2342
+ part: "names",
2343
+ title: "Names",
2344
+ reaches: "The values that a binding gives: a let, a parameter, or the result of an earlier step.",
2345
+ direction: "Back, to the place where the program binds the name."
2346
+ },
2347
+ {
2348
+ part: "world",
2349
+ title: "World",
2350
+ reaches: "The stored documents and collections, by an absolute ID or by a query.",
2351
+ direction: "Out, to the store."
2352
+ },
2353
+ {
2354
+ part: "event",
2355
+ title: "Event",
2356
+ reaches: "The trigger of a run: the event and its values.",
2357
+ direction: "From the past, to the run that the event starts."
2358
+ },
2359
+ {
2360
+ part: "caps",
2361
+ title: "Capabilities",
2362
+ reaches: "The effects that a term can cause. A part of the term gets the same set or a smaller set.",
2363
+ direction: "Down, and only smaller."
2364
+ }
2365
+ ];
2366
+ const NONE = {
2367
+ reads: false,
2368
+ gives: "none",
2369
+ note: "It does not use this part."
2370
+ };
2371
+ /** Six constructs that show the range of the context. */
2372
+ const CONSTRUCTS = [
2373
+ {
2374
+ id: "formula",
2375
+ title: "Formula",
2376
+ side: "derive",
2377
+ summary: "A pure formula that gives a value at one position of a structure.",
2378
+ example: {
2379
+ type: "let",
2380
+ value: {
2381
+ bind: { gap: 8 },
2382
+ body: {
2383
+ type: ["ops", "add"],
2384
+ value: [{
2385
+ type: "ref",
2386
+ value: [
2387
+ "self",
2388
+ "parent",
2389
+ "width"
2390
+ ]
2391
+ }, {
2392
+ type: "ref",
2393
+ value: ["names", "gap"]
2394
+ }]
2395
+ }
2396
+ }
2397
+ },
2398
+ uses: {
2399
+ names: {
2400
+ reads: true,
2401
+ gives: "extends",
2402
+ note: "A let binds names for its body."
2403
+ },
2404
+ self: {
2405
+ reads: true,
2406
+ gives: "moves",
2407
+ note: "A move reads the parent. An axis moves self to each target."
2408
+ },
2409
+ world: NONE,
2410
+ event: NONE,
2411
+ caps: {
2412
+ reads: false,
2413
+ gives: "none",
2414
+ note: "A formula causes no effect, thus its parts get no capability."
2415
+ }
2416
+ }
2417
+ },
2418
+ {
2419
+ id: "sequence",
2420
+ title: "Sequence",
2421
+ side: "run",
2422
+ summary: "A list of steps. A step can read the result of each earlier step by its name.",
2423
+ example: {
2424
+ type: "seq",
2425
+ value: [{
2426
+ type: ["store", "find"],
2427
+ value: { match: { board: "b1" } },
2428
+ name: "cards"
2429
+ }, {
2430
+ type: ["store", "patch"],
2431
+ value: {
2432
+ ids: {
2433
+ type: "ref",
2434
+ value: [
2435
+ "names",
2436
+ "cards",
2437
+ "ids"
2438
+ ]
2439
+ },
2440
+ set: { done: true }
2441
+ }
2442
+ }]
2443
+ },
2444
+ uses: {
2445
+ names: {
2446
+ reads: true,
2447
+ gives: "extends",
2448
+ note: "The result of each step is a name for the later steps."
2449
+ },
2450
+ self: NONE,
2451
+ world: {
2452
+ reads: true,
2453
+ gives: "same",
2454
+ note: "A step reads and writes the store through a procedure."
2455
+ },
2456
+ event: {
2457
+ reads: false,
2458
+ gives: "same",
2459
+ note: "The steps get the event of the run without a change."
2460
+ },
2461
+ caps: {
2462
+ reads: true,
2463
+ gives: "narrows",
2464
+ note: "A nested call gets the same capabilities or fewer."
2465
+ }
2466
+ }
2467
+ },
2468
+ {
2469
+ id: "node",
2470
+ title: "Tree node",
2471
+ side: "derive",
2472
+ summary: "A node of a document tree. Its data and its box go down to its children.",
2473
+ example: {
2474
+ type: "node",
2475
+ value: {
2476
+ data: { title: "Notes" },
2477
+ children: [{
2478
+ type: "text",
2479
+ value: {
2480
+ type: "ref",
2481
+ value: ["names", "title"]
2482
+ }
2483
+ }]
2484
+ }
2485
+ },
2486
+ uses: {
2487
+ names: {
2488
+ reads: true,
2489
+ gives: "extends",
2490
+ note: "The data of the node becomes a name for its children."
2491
+ },
2492
+ self: {
2493
+ reads: true,
2494
+ gives: "moves",
2495
+ note: "Each child becomes self for its own part."
2496
+ },
2497
+ world: {
2498
+ reads: true,
2499
+ gives: "same",
2500
+ note: "A node can show stored documents."
2501
+ },
2502
+ event: NONE,
2503
+ caps: NONE
2504
+ }
2505
+ },
2506
+ {
2507
+ id: "rule",
2508
+ title: "Layout rule",
2509
+ side: "derive",
2510
+ summary: "A rule that gives the box of a child from the box of its parent.",
2511
+ example: {
2512
+ type: ["layouts", "stack"],
2513
+ value: { gap: 8 }
2514
+ },
2515
+ uses: {
2516
+ names: {
2517
+ reads: true,
2518
+ gives: "none",
2519
+ note: "The rule reads its parameters."
2520
+ },
2521
+ self: {
2522
+ reads: true,
2523
+ gives: "none",
2524
+ note: "The rule reads the box of the parent and the key of the child."
2525
+ },
2526
+ world: NONE,
2527
+ event: NONE,
2528
+ caps: NONE
2529
+ }
2530
+ },
2531
+ {
2532
+ id: "handler",
2533
+ title: "Event handler",
2534
+ side: "run",
2535
+ summary: "A run that an event starts. It holds a sequence as data until the event comes.",
2536
+ example: {
2537
+ type: "on",
2538
+ value: {
2539
+ event: "click",
2540
+ run: {
2541
+ type: "seq",
2542
+ value: [{
2543
+ type: ["card", "toggle"],
2544
+ value: { id: {
2545
+ type: "ref",
2546
+ value: ["event", "target"]
2547
+ } }
2548
+ }]
2549
+ }
2550
+ }
2551
+ },
2552
+ uses: {
2553
+ names: {
2554
+ reads: true,
2555
+ gives: "same",
2556
+ note: "The handler reads the names of its place."
2557
+ },
2558
+ self: {
2559
+ reads: true,
2560
+ gives: "same",
2561
+ note: "The handler knows the node that got the event."
2562
+ },
2563
+ world: {
2564
+ reads: true,
2565
+ gives: "same",
2566
+ note: "The run reads and writes the store."
2567
+ },
2568
+ event: {
2569
+ reads: true,
2570
+ gives: "sets",
2571
+ note: "The handler gives the event to its run."
2572
+ },
2573
+ caps: {
2574
+ reads: true,
2575
+ gives: "narrows",
2576
+ note: "The run gets only the capabilities of the handler."
2577
+ }
2578
+ }
2579
+ },
2580
+ {
2581
+ id: "query",
2582
+ title: "Query",
2583
+ side: "derive",
2584
+ summary: "A selection of stored documents. A storage adapter decides where it runs.",
2585
+ example: {
2586
+ type: "query",
2587
+ value: {
2588
+ collection: "cards",
2589
+ match: { board: {
2590
+ type: "ref",
2591
+ value: ["names", "board"]
2592
+ } }
2593
+ }
2594
+ },
2595
+ uses: {
2596
+ names: {
2597
+ reads: true,
2598
+ gives: "none",
2599
+ note: "The query reads its parameters."
2600
+ },
2601
+ self: NONE,
2602
+ world: {
2603
+ reads: true,
2604
+ gives: "none",
2605
+ note: "The query reads one collection."
2606
+ },
2607
+ event: NONE,
2608
+ caps: NONE
2609
+ }
2610
+ }
2611
+ ];
2612
+ //#endregion
2613
+ //#region src/static.ts
2614
+ function valueAt(value, path) {
2615
+ let here = value;
2616
+ for (const key of path) if (Array.isArray(here)) here = here[Number(key)];
2617
+ else if (isRecord(here) && Object.hasOwn(here, String(key))) here = here[String(key)];
2618
+ else return void 0;
2619
+ return here;
2620
+ }
2621
+ function headerName(item) {
2622
+ return isEnvelope(item) && typeof item.name === "string" ? item.name : void 0;
2623
+ }
2624
+ function namesOf(source, value, covered) {
2625
+ if ("fixed" in source) return source.fixed;
2626
+ if ("keysAt" in source) {
2627
+ const at = valueAt(value, source.keysAt);
2628
+ return isRecord(at) ? Object.keys(at) : [];
2629
+ }
2630
+ if ("listAt" in source) {
2631
+ const at = valueAt(value, source.listAt);
2632
+ return Array.isArray(at) ? at.filter((item) => typeof item === "string") : [];
2633
+ }
2634
+ if ("textAt" in source) {
2635
+ const at = valueAt(value, source.textAt);
2636
+ if (typeof at === "string") return [at];
2637
+ return source.otherwise === void 0 ? [] : [source.otherwise];
2638
+ }
2639
+ return covered.map((slot) => headerName(slot.item)).filter((name) => name !== void 0);
2640
+ }
2641
+ /** The names that the scope rules of a kind give each part, and the names that only later parts see. */
2642
+ function scopesOf(rules, value, slots) {
2643
+ const sees = slots.map(() => []);
2644
+ const later = slots.map(() => []);
2645
+ for (const rule of rules ?? []) {
2646
+ const covered = slots.map((slot, index) => ({
2647
+ slot,
2648
+ index
2649
+ })).filter(({ slot }) => rule.in.some((pattern) => pathMatches(slot.path, pattern)));
2650
+ if (rule.sequential !== true) {
2651
+ const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
2652
+ for (const { index } of covered) sees[index]?.push(...names);
2653
+ continue;
2654
+ }
2655
+ if ("headers" in rule.names) {
2656
+ covered.forEach(({ index }, position) => {
2657
+ const before = covered.slice(0, position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
2658
+ const after = covered.slice(position).map(({ slot }) => headerName(slot.item)).filter((name) => name !== void 0);
2659
+ sees[index]?.push(...before);
2660
+ later[index]?.push(...after);
2661
+ });
2662
+ continue;
2663
+ }
2664
+ const names = namesOf(rule.names, value, covered.map(({ slot }) => slot));
2665
+ covered.forEach(({ index }, position) => {
2666
+ if (position > 0) sees[index]?.push(...names);
2667
+ else later[index]?.push(...names);
2668
+ });
2669
+ }
2670
+ return {
2671
+ sees,
2672
+ later
2673
+ };
2674
+ }
2675
+ /**
2676
+ * This function visits each document of a document: the root and each part at each depth, by the contracts of the
2677
+ * 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
2678
+ * depth, thus a deep document cannot overflow it.
2679
+ *
2680
+ * @param world - The world of the kinds.
2681
+ * @param doc - The document, as JSON.
2682
+ * @param visit - The function that gets each document. It gives `"skip"` to not visit the parts of a document.
2683
+ * @param options - The type of a bare root value, the names of the host, and a function for the problems.
2684
+ */
2685
+ function walk(world, doc, visit, options = {}) {
2686
+ const stack = [{
2687
+ value: doc,
2688
+ place: [],
2689
+ at: [],
2690
+ declared: options.declaredType,
2691
+ depth: 0,
2692
+ names: new Set(options.names ?? []),
2693
+ later: /* @__PURE__ */ new Set()
2694
+ }];
2695
+ while (stack.length > 0) {
2696
+ const item = stack.pop();
2697
+ const envelope = isEnvelope(item.value) ? item.value : void 0;
2698
+ if (envelope === void 0 && item.declared === void 0) {
2699
+ options.problem?.({
2700
+ place: item.place,
2701
+ at: item.at,
2702
+ code: "#PART?",
2703
+ reason: "The value has no envelope, and its position declares no type."
2704
+ });
2705
+ continue;
2706
+ }
2707
+ const doc = envelope ?? {
2708
+ type: item.declared ?? "?",
2709
+ value: item.value
2710
+ };
2711
+ const found = lookupKind(world, doc.type);
2712
+ const kind = found.ok ? found.kind : void 0;
2713
+ if (visit({
2714
+ place: item.place,
2715
+ at: item.at,
2716
+ doc,
2717
+ declared: envelope === void 0,
2718
+ ...kind === void 0 ? {} : {
2719
+ kind,
2720
+ typePath: found.ok ? found.path : []
2721
+ },
2722
+ depth: item.depth,
2723
+ names: item.names,
2724
+ later: item.later
2725
+ }) === "skip" || kind === void 0) continue;
2726
+ const split = splitParts(kind.parts, doc.value);
2727
+ if (!split.ok) {
2728
+ const at = split.at.length === 0 ? "" : ` (at ${split.at.join(".")})`;
2729
+ options.problem?.({
2730
+ place: item.place,
2731
+ at: item.at,
2732
+ code: "#VALUE!",
2733
+ reason: `${split.reason}${at} The kind is ${typeName(doc.type)}.`
2734
+ });
2735
+ continue;
2736
+ }
2737
+ const { sees, later } = scopesOf(kind.scope, doc.value, split.parts);
2738
+ const base = envelope === void 0 ? item.at : [...item.at, "value"];
2739
+ for (let index = split.parts.length - 1; index >= 0; index -= 1) {
2740
+ const slot = split.parts[index];
2741
+ const extra = sees[index] ?? [];
2742
+ const hidden = later[index] ?? [];
2743
+ stack.push({
2744
+ value: slot.item,
2745
+ place: [...item.place, ...slot.path],
2746
+ at: [...base, ...slot.path],
2747
+ declared: slot.of,
2748
+ depth: item.depth + 1,
2749
+ names: extra.length === 0 ? item.names : /* @__PURE__ */ new Set([...item.names, ...extra]),
2750
+ later: hidden.length === 0 ? item.later : /* @__PURE__ */ new Set([...item.later, ...hidden])
2751
+ });
2752
+ }
2753
+ }
2754
+ }
2755
+ /**
2756
+ * This function finds each reference of a document: each document of the type `ref` that a walk reaches. A
2757
+ * reference to a name that a kind around it binds is not free. The others are what the document reads from
2758
+ * outside: its dependencies.
2759
+ *
2760
+ * @param world - The world of the kinds.
2761
+ * @param doc - The document, as JSON.
2762
+ * @param options - The type of a bare root value. The names of the host do not change the result.
2763
+ * @returns The references, in the order of the document.
2764
+ */
2765
+ function refsOf(world, doc, options = {}) {
2766
+ const out = [];
2767
+ const { names: _host, ...rest } = options;
2768
+ walk(world, doc, (v) => {
2769
+ if (!isRef(v.doc)) return void 0;
2770
+ const path = v.doc.value;
2771
+ const root = typeof path[0] === "string" ? path[0] : "";
2772
+ const first = path[1];
2773
+ const bound = root === "names" && isKey(first) && v.names.has(String(first));
2774
+ out.push({
2775
+ place: v.place,
2776
+ at: v.at,
2777
+ path,
2778
+ root,
2779
+ free: !bound
2780
+ });
2781
+ return "skip";
2782
+ }, rest);
2783
+ return out;
2784
+ }
2785
+ function setAt(value, path, next) {
2786
+ if (path.length === 0) return next;
2787
+ const [key, ...rest] = path;
2788
+ if (Array.isArray(value)) {
2789
+ const copy = [...value];
2790
+ copy[Number(key)] = setAt(value[Number(key)], rest, next);
2791
+ return copy;
2792
+ }
2793
+ const record = isRecord(value) ? value : {};
2794
+ const copy = {};
2795
+ for (const name of Object.keys(record)) Object.defineProperty(copy, name, {
2796
+ value: record[name],
2797
+ enumerable: true,
2798
+ writable: true,
2799
+ configurable: true
2800
+ });
2801
+ Object.defineProperty(copy, String(key), {
2802
+ value: setAt(record[String(key)], rest, next),
2803
+ enumerable: true,
2804
+ writable: true,
2805
+ configurable: true
2806
+ });
2807
+ return copy;
2808
+ }
2809
+ /**
2810
+ * This function changes the free references of a document. The document is not changed: the function gives a new
2811
+ * document that shares each part without a change.
2812
+ *
2813
+ * @param world - The world of the kinds.
2814
+ * @param doc - The document, as JSON.
2815
+ * @param change - The function that gives the new document of a reference, or `undefined` to keep it.
2816
+ * @param options - The type of a bare root value, and the names that the host binds at the root.
2817
+ * @returns The new document.
2818
+ */
2819
+ function mapRefs(world, doc, change, options = {}) {
2820
+ let out = doc;
2821
+ for (const use of refsOf(world, doc, options)) {
2822
+ if (!use.free) continue;
2823
+ const next = change(use);
2824
+ if (next !== void 0) out = setAt(out, use.at, next);
2825
+ }
2826
+ return out;
2827
+ }
2828
+ /**
2829
+ * This function checks a document before an evaluation, by the contracts of its kinds. It finds the problems
2830
+ * that an evaluation finds when it reaches them:
2831
+ *
2832
+ * - `#KIND?`: a kind that the world does not have.
2833
+ * - `#NOEVAL`: no evaluator.
2834
+ * - `#VALUE!`: a value without the shape of its kind.
2835
+ * - `#PART?`: a value without a type.
2836
+ * - `#DEPTH!`: too much nesting.
2837
+ * - `#REF!` and `#STEP?`: a bad path.
2838
+ * - `#NAME?`: a name that nothing binds.
2839
+ * - `#CAP!`: an effect that the capabilities do not permit.
2840
+ *
2841
+ * @param machine - The world and the code of the evaluators.
2842
+ * @param doc - The document, as JSON.
2843
+ * @param options - The type of a bare root value, the names of the host and the capabilities.
2844
+ * @returns The problems, in the order of the document.
2845
+ */
2846
+ function check(machine, doc, options = {}) {
2847
+ const issues = [];
2848
+ const maxDepth = machine.maxDepth ?? 1e3;
2849
+ const anyName = options.names === "any";
2850
+ const roots = machine.roots ?? ROOTS;
2851
+ const has = (table, name) => table !== void 0 && Object.hasOwn(table, name);
2852
+ walk(machine.world, doc, (v) => {
2853
+ if (v.depth > maxDepth) {
2854
+ issues.push({
2855
+ code: "#DEPTH!",
2856
+ place: v.place,
2857
+ reason: `The nesting is deeper than ${maxDepth} documents.`
2858
+ });
2859
+ return "skip";
2860
+ }
2861
+ const header = v.declared ? {} : v.doc;
2862
+ const answer = ask(machine.world, header, "eval", v.declared ? { declaredType: typeName(v.doc.type) } : {});
2863
+ if (answer.error === "unknown-kind") {
2864
+ issues.push({
2865
+ code: "#KIND?",
2866
+ place: v.place,
2867
+ reason: `The world has no kind ${typeName(v.doc.type)}.`
2868
+ });
2869
+ return "skip";
2870
+ }
2871
+ const name = answer.evaluator;
2872
+ if (name === void 0 || !has(machine.evaluators, name) && !has(machine.runners, name)) issues.push({
2873
+ code: "#NOEVAL",
2874
+ place: v.place,
2875
+ reason: `No rung gives an evaluator for ${typeName(v.doc.type)}.`
2876
+ });
2877
+ if (options.caps !== void 0 && v.kind?.effects !== void 0) {
2878
+ const kinds = v.kind.effects === "type" ? [v.typePath ?? []] : v.kind.effects.map((kind) => kind.split("."));
2879
+ for (const kind of kinds) if (!permits(options.caps, kind)) issues.push({
2880
+ code: "#CAP!",
2881
+ place: v.place,
2882
+ reason: `The capabilities do not permit the effect ${kind.join(".")}.`
2883
+ });
2884
+ }
2885
+ if (!isRef(v.doc)) return void 0;
2886
+ const path = v.doc.value;
2887
+ const root = path[0];
2888
+ if (typeof root !== "string" || !ROOTS.includes(root)) {
2889
+ issues.push({
2890
+ code: "#REF!",
2891
+ place: v.place,
2892
+ reason: `A path starts with a root: ${ROOTS.join(", ")}.`
2893
+ });
2894
+ return "skip";
2895
+ }
2896
+ if (!roots.includes(root)) issues.push({
2897
+ code: "#REF!",
2898
+ place: v.place,
2899
+ reason: `The root ${root} is not open here. The open roots are ${roots.join(", ")}.`
2900
+ });
2901
+ const first = path[1];
2902
+ if (root === "names" && !anyName && isKey(first) && !v.names.has(String(first))) {
2903
+ const later = v.later.has(String(first));
2904
+ issues.push({
2905
+ code: "#NAME?",
2906
+ place: v.place,
2907
+ reason: later ? `The name ${String(first)} is bound by a later step.` : `Nothing binds the name ${String(first)}.`
2908
+ });
2909
+ }
2910
+ path.slice(1).forEach((step) => {
2911
+ if (isKey(step)) return;
2912
+ const found = isEnvelope(step) ? lookupKind(machine.world, step.type) : void 0;
2913
+ const walker = found?.ok === true && Object.hasOwn(found.kind.verbs, "step") ? found.kind.verbs["step"] : void 0;
2914
+ if (walker === void 0 || !has(machine.walkers, walker)) issues.push({
2915
+ code: "#STEP?",
2916
+ place: v.place,
2917
+ reason: "A step is a key, or a document of a kind with the verb step."
2918
+ });
2919
+ });
2920
+ return "skip";
2921
+ }, {
2922
+ ...options.declaredType === void 0 ? {} : { declaredType: options.declaredType },
2923
+ ...Array.isArray(options.names) ? { names: options.names } : {},
2924
+ problem: (problem) => issues.push({
2925
+ code: problem.code,
2926
+ place: problem.place,
2927
+ reason: problem.reason
2928
+ })
2929
+ });
2930
+ return issues;
2931
+ }
2932
+ //#endregion
2933
+ //#region src/formula.ts
2934
+ /** The value of a name whose binding failed. A reference to the name gives the error. */
2935
+ var Held = class {
2936
+ error;
2937
+ constructor(error) {
2938
+ this.error = error;
2939
+ }
2940
+ };
2941
+ const CLOSURE = Symbol.for("coeval.closure");
2942
+ /**
2943
+ * This function tells if a value is a function that the construct `fn` made.
2944
+ *
2945
+ * @param value - The value.
2946
+ * @returns `true` for a closure.
2947
+ */
2948
+ function isClosure(value) {
2949
+ return typeof value === "function" && value[CLOSURE] !== void 0;
2950
+ }
2951
+ /** The standard extensions: the values that JSON cannot write. */
2952
+ const STD_EXT = {
2953
+ undefined: () => ok(void 0),
2954
+ number: (data) => {
2955
+ if (data === "NaN") return ok(NaN);
2956
+ if (data === "Infinity") return ok(Number.POSITIVE_INFINITY);
2957
+ if (data === "-Infinity") return ok(Number.NEGATIVE_INFINITY);
2958
+ if (data === "-0") return ok(-0);
2959
+ return fail("#VALUE!", "The value of ext.number is NaN, Infinity, -Infinity or -0.");
2960
+ }
2961
+ };
2962
+ /** The truth of a value in the rules of JavaScript: `0`, `""`, `null`, `undefined` and `NaN` are false. */
2963
+ function jsTruthy(value) {
2964
+ return Boolean(value);
2965
+ }
2966
+ function strictTruthy(value) {
2967
+ return typeof value === "boolean" ? value : void 0;
2968
+ }
2969
+ function setOwn(target, key, value) {
2970
+ Object.defineProperty(target, key, {
2971
+ value,
2972
+ enumerable: true,
2973
+ writable: true,
2974
+ configurable: true
2975
+ });
2976
+ }
2977
+ function compiled(compile) {
2978
+ return { compile };
2979
+ }
2980
+ function partsOf(value) {
2981
+ return isRecord(value) ? value : {};
2982
+ }
2983
+ const LIT_SUMMARY = "A literal: its value is the result.";
2984
+ /**
2985
+ * This function makes the kinds of the formula kit, for a world. The kinds are the shared constructs of D13 and
2986
+ * the namespaces `ops` and `ext`. A bare value in a position of a term is a literal.
2987
+ *
2988
+ * @returns The entries of the world, by their key.
2989
+ */
2990
+ function formulaKinds() {
2991
+ const term = {
2992
+ shape: "term",
2993
+ of: "lit"
2994
+ };
2995
+ const terms = {
2996
+ shape: "list",
2997
+ of: "lit"
2998
+ };
2999
+ return {
3000
+ lit: {
3001
+ verbs: { eval: "formula.lit" },
3002
+ summary: LIT_SUMMARY
3003
+ },
3004
+ ref: {
3005
+ verbs: { eval: "formula.ref" },
3006
+ summary: "A path from a root of the context. Its result is what the path reaches."
3007
+ },
3008
+ record: {
3009
+ verbs: { eval: "formula.record" },
3010
+ parts: {
3011
+ shape: "map",
3012
+ of: "lit"
3013
+ },
3014
+ summary: "A record: each field is a term."
3015
+ },
3016
+ list: {
3017
+ verbs: { eval: "formula.list" },
3018
+ parts: terms,
3019
+ summary: "A list: each item is a term."
3020
+ },
3021
+ let: {
3022
+ verbs: { eval: "formula.let" },
3023
+ parts: {
3024
+ shape: "record",
3025
+ fields: {
3026
+ bind: {
3027
+ shape: "map",
3028
+ of: "lit"
3029
+ },
3030
+ body: term
3031
+ },
3032
+ required: ["bind", "body"]
3033
+ },
3034
+ scope: [{
3035
+ names: { keysAt: ["bind"] },
3036
+ in: [["body"]]
3037
+ }],
3038
+ summary: "Names for the body. Each binding sees the names around the let, not the other bindings."
3039
+ },
3040
+ fn: {
3041
+ verbs: { eval: "formula.fn" },
3042
+ parts: {
3043
+ shape: "record",
3044
+ fields: { body: term },
3045
+ required: ["params", "body"]
3046
+ },
3047
+ scope: [{
3048
+ names: { listAt: ["params"] },
3049
+ in: [["body"]]
3050
+ }],
3051
+ summary: "A function: its parameters are names for its body."
3052
+ },
3053
+ call: {
3054
+ verbs: { eval: "formula.call" },
3055
+ parts: {
3056
+ shape: "record",
3057
+ fields: {
3058
+ fn: term,
3059
+ args: terms
3060
+ },
3061
+ required: ["fn"]
3062
+ },
3063
+ summary: "It calls a function with the values of its arguments."
3064
+ },
3065
+ if: {
3066
+ verbs: { eval: "formula.if" },
3067
+ parts: {
3068
+ shape: "record",
3069
+ fields: {
3070
+ test: term,
3071
+ then: term,
3072
+ else: term
3073
+ },
3074
+ required: ["test", "then"]
3075
+ },
3076
+ summary: "It evaluates the test, then only the branch that the test selects."
3077
+ },
3078
+ and: {
3079
+ verbs: { eval: "formula.and" },
3080
+ parts: terms,
3081
+ summary: "True when each term is true. It stops at the first false term."
3082
+ },
3083
+ or: {
3084
+ verbs: { eval: "formula.or" },
3085
+ parts: terms,
3086
+ summary: "True when one term is true. It stops at the first true term."
3087
+ },
3088
+ ops: {
3089
+ kinds: {},
3090
+ fallback: {
3091
+ verbs: { eval: "formula.op" },
3092
+ parts: terms,
3093
+ summary: "An operation of the language, applied to the values of its terms."
3094
+ }
3095
+ },
3096
+ ext: {
3097
+ kinds: {},
3098
+ fallback: {
3099
+ verbs: { eval: "formula.ext" },
3100
+ summary: "An extension of the language. Its value is data."
3101
+ }
3102
+ }
3103
+ };
3104
+ }
3105
+ /**
3106
+ * This function makes the evaluators of the formula kit for one language. Each evaluator compiles its document
3107
+ * one time into code.
3108
+ *
3109
+ * @param profile - The operations and the rules of the language.
3110
+ * @returns The evaluators, by their name.
3111
+ */
3112
+ function formulaEvaluators(profile = {}) {
3113
+ const truthy = profile.truthy ?? strictTruthy;
3114
+ const otherwise = ok(profile.otherwise ?? null);
3115
+ const logicValue = profile.logic === "value";
3116
+ const omit = profile.omit;
3117
+ const missing = profile.missing;
3118
+ const check = profile.check;
3119
+ const lookup = (name) => {
3120
+ const ops = profile.ops;
3121
+ if (ops === void 0) return void 0;
3122
+ if (typeof ops === "function") return ops(name);
3123
+ return Object.hasOwn(ops, name) ? ops[name] : void 0;
3124
+ };
3125
+ const notTruth = (what) => fail("#VALUE!", `The ${what} must give a truth value.`);
3126
+ return {
3127
+ "formula.lit": compiled(({ doc }) => {
3128
+ const result = ok(doc.value);
3129
+ return () => result;
3130
+ }),
3131
+ "formula.ref": compiled(({ doc }) => {
3132
+ const path = doc.value;
3133
+ if (!Array.isArray(path) || path.length === 0) {
3134
+ const bad = fail("#VALUE!", "The value of ref is a path: a list that starts with a root, for example [\"names\", \"a\"].");
3135
+ return () => bad;
3136
+ }
3137
+ const head = path[0] === "names" && typeof path[1] === "string" ? path[1] : void 0;
3138
+ return (ctx, rt) => {
3139
+ if (head !== void 0 && Object.hasOwn(ctx.names, head)) {
3140
+ const bound = ctx.names[head];
3141
+ if (bound instanceof Held) return {
3142
+ ok: false,
3143
+ error: bound.error
3144
+ };
3145
+ }
3146
+ const found = rt.resolve(ctx, path);
3147
+ if (!found.ok) return fail(found.code, found.reason);
3148
+ const value = valueOfThing(found.thing);
3149
+ if (value instanceof Held) return {
3150
+ ok: false,
3151
+ error: value.error
3152
+ };
3153
+ if (missing !== void 0 && missing(value)) return fail("#N/A", "The path reaches no value.");
3154
+ return ok(value);
3155
+ };
3156
+ }),
3157
+ "formula.record": compiled(({ value }) => {
3158
+ const fields = Object.entries(partsOf(value));
3159
+ return (ctx, rt) => {
3160
+ const out = {};
3161
+ for (const [key, part] of fields) {
3162
+ const result = part.code(ctx, rt);
3163
+ if (!result.ok) {
3164
+ if (omit !== void 0 && omit(result.error)) continue;
3165
+ return result;
3166
+ }
3167
+ setOwn(out, key, result.value);
3168
+ }
3169
+ return ok(out);
3170
+ };
3171
+ }),
3172
+ "formula.list": compiled(({ parts }) => (ctx, rt) => {
3173
+ const out = new Array(parts.length);
3174
+ for (let index = 0; index < parts.length; index += 1) {
3175
+ const result = parts[index].code(ctx, rt);
3176
+ if (!result.ok) return result;
3177
+ out[index] = result.value;
3178
+ }
3179
+ return ok(out);
3180
+ }),
3181
+ "formula.let": compiled(({ value }) => {
3182
+ const record = partsOf(value);
3183
+ const bindings = Object.entries(partsOf(record["bind"]));
3184
+ const body = record["body"];
3185
+ return (ctx, rt) => {
3186
+ const names = { ...ctx.names };
3187
+ for (const [name, part] of bindings) {
3188
+ const result = part.code(ctx, rt);
3189
+ setOwn(names, name, result.ok ? result.value : new Held(result.error));
3190
+ }
3191
+ return body.code({
3192
+ ...ctx,
3193
+ names
3194
+ }, rt);
3195
+ };
3196
+ }),
3197
+ "formula.fn": compiled(({ value, data }) => {
3198
+ const params = isRecord(data) ? data["params"] : void 0;
3199
+ if (!Array.isArray(params) || !params.every((param) => typeof param === "string") || new Set(params).size !== params.length) {
3200
+ const bad = fail("#VALUE!", "The params of fn are a list of different texts.");
3201
+ return () => bad;
3202
+ }
3203
+ const names = params;
3204
+ const body = partsOf(value)["body"];
3205
+ return (ctx, rt) => {
3206
+ const closure = (...args) => {
3207
+ const scope = { ...ctx.names };
3208
+ names.forEach((name, index) => setOwn(scope, name, args[index]));
3209
+ const result = body.code({
3210
+ ...ctx,
3211
+ names: scope
3212
+ }, rt);
3213
+ if (!result.ok) throw new Failure(result.error);
3214
+ return result.value;
3215
+ };
3216
+ Object.defineProperty(closure, CLOSURE, { value: { params: names } });
3217
+ return ok(closure);
3218
+ };
3219
+ }),
3220
+ "formula.call": compiled(({ value }) => {
3221
+ const record = partsOf(value);
3222
+ const fnPart = record["fn"];
3223
+ const args = Array.isArray(record["args"]) ? record["args"] : [];
3224
+ return (ctx, rt) => {
3225
+ const fn = fnPart.code(ctx, rt);
3226
+ if (!fn.ok) return fn;
3227
+ if (typeof fn.value !== "function") return fail("#VALUE!", "The fn of call must give a function.");
3228
+ const values = new Array(args.length);
3229
+ for (let index = 0; index < args.length; index += 1) {
3230
+ const result = args[index].code(ctx, rt);
3231
+ if (!result.ok) return result;
3232
+ values[index] = result.value;
3233
+ }
3234
+ try {
3235
+ return ok(fn.value(...values));
3236
+ } catch (error) {
3237
+ return thrown(error, "the function of call");
3238
+ }
3239
+ };
3240
+ }),
3241
+ "formula.if": compiled(({ value }) => {
3242
+ const record = partsOf(value);
3243
+ const test = record["test"];
3244
+ const then = record["then"];
3245
+ const other = record["else"];
3246
+ return (ctx, rt) => {
3247
+ const chosen = test.code(ctx, rt);
3248
+ if (!chosen.ok) return chosen;
3249
+ const truth = truthy(chosen.value);
3250
+ if (truth === void 0) return notTruth("test of if");
3251
+ if (truth) return then.code(ctx, rt);
3252
+ return other === void 0 ? otherwise : other.code(ctx, rt);
3253
+ };
3254
+ }),
3255
+ "formula.and": compiled(({ parts }) => (ctx, rt) => {
3256
+ let last = ok(true);
3257
+ for (const part of parts) {
3258
+ last = part.code(ctx, rt);
3259
+ if (!last.ok) return last;
3260
+ const truth = truthy(last.value);
3261
+ if (truth === void 0) return notTruth("term of and");
3262
+ if (!truth) return logicValue ? last : ok(false);
3263
+ }
3264
+ return logicValue ? last : ok(true);
3265
+ }),
3266
+ "formula.or": compiled(({ parts }) => (ctx, rt) => {
3267
+ let last = ok(false);
3268
+ for (const part of parts) {
3269
+ last = part.code(ctx, rt);
3270
+ if (!last.ok) return last;
3271
+ const truth = truthy(last.value);
3272
+ if (truth === void 0) return notTruth("term of or");
3273
+ if (truth) return logicValue ? last : ok(true);
3274
+ }
3275
+ return logicValue ? last : ok(false);
3276
+ }),
3277
+ "formula.op": compiled(({ typePath, parts }) => {
3278
+ const name = typePath.slice(1).join(".");
3279
+ const lazy = profile.lazy !== void 0 && Object.hasOwn(profile.lazy, name) ? profile.lazy[name] : void 0;
3280
+ if (lazy !== void 0) return (ctx, rt) => {
3281
+ try {
3282
+ return lazy(parts.map((part) => () => part.code(ctx, rt)));
3283
+ } catch (error) {
3284
+ return thrown(error, `the operation ${name}`);
3285
+ }
3286
+ };
3287
+ const fn = lookup(name);
3288
+ if (fn === void 0) {
3289
+ const unknown = fail("#NAME?", `The language has no operation ${name}.`);
3290
+ return () => unknown;
3291
+ }
3292
+ const done = (out) => check === void 0 ? ok(out) : check(out, name) ?? ok(out);
3293
+ const failed = (error) => thrown(error, `the operation ${name}`);
3294
+ if (parts.length === 1) {
3295
+ const [a] = parts;
3296
+ return (ctx, rt) => {
3297
+ const x = a.code(ctx, rt);
3298
+ if (!x.ok) return x;
3299
+ try {
3300
+ return done(fn(x.value));
3301
+ } catch (error) {
3302
+ return failed(error);
3303
+ }
3304
+ };
3305
+ }
3306
+ if (parts.length === 2) {
3307
+ const [a, b] = parts;
3308
+ return (ctx, rt) => {
3309
+ const x = a.code(ctx, rt);
3310
+ if (!x.ok) return x;
3311
+ const y = b.code(ctx, rt);
3312
+ if (!y.ok) return y;
3313
+ try {
3314
+ return done(fn(x.value, y.value));
3315
+ } catch (error) {
3316
+ return failed(error);
3317
+ }
3318
+ };
3319
+ }
3320
+ return (ctx, rt) => {
3321
+ const values = new Array(parts.length);
3322
+ for (let index = 0; index < parts.length; index += 1) {
3323
+ const result = parts[index].code(ctx, rt);
3324
+ if (!result.ok) return result;
3325
+ values[index] = result.value;
3326
+ }
3327
+ try {
3328
+ return done(fn(...values));
3329
+ } catch (error) {
3330
+ return failed(error);
3331
+ }
3332
+ };
3333
+ }),
3334
+ "formula.ext": compiled(({ typePath, doc }) => {
3335
+ const name = typePath.slice(1).join(".");
3336
+ 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);
3337
+ if (handler === void 0) {
3338
+ const unknown = fail("#NAME?", `The language has no extension ${name}.`);
3339
+ return () => unknown;
3340
+ }
3341
+ return () => {
3342
+ try {
3343
+ return handler(doc.value);
3344
+ } catch (error) {
3345
+ return thrown(error, `the extension ${name}`);
3346
+ }
3347
+ };
3348
+ })
3349
+ };
3350
+ }
3351
+ /**
3352
+ * This function makes a machine for a formula language. The machine has the kinds and the evaluators of the
3353
+ * formula kit. It also has the kinds, the evaluators and the walkers that the language adds.
3354
+ *
3355
+ * @param profile - The operations and the rules of the language.
3356
+ * @param more - The parts of a machine that the language adds. Its kinds win over the kinds of the kit.
3357
+ * @returns The machine.
3358
+ */
3359
+ function formulaMachine(profile = {}, more = {}) {
3360
+ return {
3361
+ ...more,
3362
+ world: {
3363
+ kinds: {
3364
+ ...formulaKinds(),
3365
+ ...more.world?.kinds
3366
+ },
3367
+ defaults: { ...more.world?.defaults }
3368
+ },
3369
+ evaluators: {
3370
+ ...formulaEvaluators(profile),
3371
+ ...more.evaluators
3372
+ }
3373
+ };
3374
+ }
3375
+ //#endregion
3376
+ 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 };
3377
+
3378
+ //# sourceMappingURL=index.js.map