@mark1russell7/coeval 0.21.0 → 0.23.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/src/types.ts CHANGED
@@ -87,7 +87,8 @@ export interface Domain {
87
87
  /**
88
88
  * The codec of a domain: it writes a member of the domain as JSON, and it reads the JSON again (D40). Both functions
89
89
  * must be pure. `decode(encode(v))` must equal `v` by `sameValue` (EXT.ROUNDTRIP). The codec must give one JSON
90
- * value for each member, thus the same value has the same document in each language (D12).
90
+ * value for each member, thus the same value has the same document in each language (D12). `codecLaws` examines
91
+ * these laws for the members of the domain (D130).
91
92
  */
92
93
  export interface DomainCodec {
93
94
  /**
package/src/world.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Envelope, type Json, type Key, type TypeRef, field, isRecord, typePath } from "./doc.ts";
1
+ import { type Envelope, type Json, type JsonRecord, type Key, type TypeRef, field, isRecord, typePath } from "./doc.ts";
2
2
  import type { Cap, Root } from "./deref.ts";
3
3
  import type { ValueType } from "./types.ts";
4
4
 
@@ -146,6 +146,8 @@ export interface KindFacts {
146
146
  * whose attributes each declare `circular` gives a fixed point.
147
147
  * - `collect`: the value of a node without an answer of the first four rungs and without a fold. The node combines
148
148
  * the contributions of each node of its subtree (TREE.COLLECT, D122).
149
+ * - `higher`: the value of a node without an answer of the rungs before it. The term gives a document as data. The kit
150
+ * evaluates that document with the node as `self`, thus its root is a new node under the node (TREE.HIGHER, D126).
149
151
  */
150
152
  export interface AttributeDeclaration {
151
153
  readonly copy?: boolean;
@@ -153,6 +155,7 @@ export interface AttributeDeclaration {
153
155
  readonly default?: Json;
154
156
  readonly circular?: CircularDeclaration;
155
157
  readonly collect?: CollectDeclaration;
158
+ readonly higher?: Json;
156
159
  }
157
160
 
158
161
  /**
@@ -189,6 +192,9 @@ export interface CircularDeclaration {
189
192
  readonly limit?: number;
190
193
  }
191
194
 
195
+ /** The most generated documents on the way from a node to the root of its tree (TREE.HIGHER-DEPTH, D126). */
196
+ export const GENERATION_LIMIT = 64;
197
+
192
198
  /** The most rounds of a circular attribute without its own `limit` (TREE.CIRCULAR-LIMIT, D113). */
193
199
  export const CIRCULAR_LIMIT = 100;
194
200
 
@@ -258,12 +264,111 @@ export function collectOf(declared: unknown): CollectRule {
258
264
  * - `attributes`: the declarations of the attributes of the node, for the rungs `copy` and `default`.
259
265
  * - `contributes`: the contribution of the node to each collection of an attribute (TREE.COLLECT, D122). An
260
266
  * equation sees no names.
267
+ * - `at`: the attributes with parameters of the node (TREE.AT, D124). Only the step `tree.at` reads them, and the
268
+ * equation sees each parameter as a name.
269
+ * - `forwards`: the name of an attribute that gives a node. An attribute that no other rung answers comes from that
270
+ * node (TREE.FORWARD, D126).
261
271
  */
262
272
  export interface TreeContract {
263
273
  readonly equations?: Readonly<Record<string, Json>>;
264
274
  readonly down?: Readonly<Record<string, Json>>;
265
275
  readonly attributes?: Readonly<Record<string, AttributeDeclaration>>;
266
276
  readonly contributes?: Readonly<Record<string, Json>>;
277
+ readonly at?: Readonly<Record<string, AtEquation>>;
278
+ readonly forwards?: string;
279
+ }
280
+
281
+ /**
282
+ * An attribute with parameters (TREE.AT, D124), as `paramAttr` in Kiama. The step `tree.at` gives the arguments. The
283
+ * equation evaluates with the node as `self`, and it sees each parameter as a name, for example `names.width`.
284
+ */
285
+ export interface AtEquation {
286
+ /** The names of the parameters. The step gives an argument for each of them, and no other argument. */
287
+ readonly params: readonly string[];
288
+ readonly equation: Json;
289
+ }
290
+
291
+ /** An entry of `tree.at` that the kit can use, or the reason of a bad entry (D124). */
292
+ export type AtRule = { readonly ok: true; readonly params: readonly string[]; readonly equation: Json } | { readonly ok: false; readonly reason: string };
293
+
294
+ /**
295
+ * This function reads an entry of the table `tree.at` (D124). The tree kit, the reference, `check` and the graph of
296
+ * attribute instances read it with this one function.
297
+ *
298
+ * @param declared - The entry.
299
+ * @returns The rule, or the reason when the entry has no correct form.
300
+ */
301
+ export function atRuleOf(declared: unknown): AtRule {
302
+ const params = isRecord(declared) ? field(declared, "params") : undefined;
303
+ const good = Array.isArray(params) && params.every((name, index) => typeof name === "string" && params.indexOf(name) === index);
304
+ if (!good || !Object.hasOwn(declared as object, "equation")) {
305
+ return { ok: false, reason: "An entry of tree.at needs a list params of different texts and the field equation." };
306
+ }
307
+ return { ok: true, params: params as readonly string[], equation: field(declared as JsonRecord, "equation") as Json };
308
+ }
309
+
310
+ /** The arguments of a step `tree.at` (D124): the names of the equation and their key, or the reason of bad arguments. */
311
+ export type AtArgs = { readonly ok: true; readonly names: Readonly<Record<string, Json>>; readonly key: string } | { readonly ok: false; readonly reason: string };
312
+
313
+ /**
314
+ * This function examines the arguments of a step `tree.at` (D124). The step gives an argument for each parameter, and
315
+ * no other argument. Each argument is JSON data. The key is the text of `argsKey`, thus two equal arguments give one key.
316
+ *
317
+ * @param params - The parameters of the attribute.
318
+ * @param args - The field `args` of the step.
319
+ * @returns The names of the equation and the key, or the reason.
320
+ */
321
+ export function atArgsOf(params: readonly string[], args: unknown): AtArgs {
322
+ if (!isRecord(args)) return { ok: false, reason: "The arguments are a record." };
323
+ const extra = Object.keys(args).find((name) => !params.includes(name));
324
+ if (extra !== undefined) return { ok: false, reason: `The attribute has no parameter ${extra}.` };
325
+ const names: Record<string, Json> = {};
326
+ for (const name of params) {
327
+ if (!Object.hasOwn(args, name)) return { ok: false, reason: `The step gives no argument ${name}.` };
328
+ const value = field(args, name);
329
+ if (argsKey(value) === undefined) return { ok: false, reason: `The argument ${name} is not JSON data.` };
330
+ Object.defineProperty(names, name, { value, enumerable: true, writable: true, configurable: true });
331
+ }
332
+ return { ok: true, names, key: argsKey(names) as string };
333
+ }
334
+
335
+ /**
336
+ * This function gives the key of a JSON value (D124). The fields of a record are in the order of their names. Two
337
+ * values have the same key only when they are equal by DOC.EQUALS, thus `-0` and `0` have different keys.
338
+ *
339
+ * @param value - The value.
340
+ * @returns The JSON text, or `undefined` for a value that is not JSON data: a number that is not finite, a host value or a cycle.
341
+ */
342
+ export function argsKey(value: unknown): string | undefined {
343
+ return keyOf(value, new Set());
344
+ }
345
+
346
+ /**
347
+ * This function gives the structural key of a document (TREE.HIGHER-PLAN, D126). The fields of a record keep their
348
+ * order, because the order of the parts decides the order of the JSON and the first error. Thus one key means one
349
+ * document, and the two documents are equal by DOC.EQUALS.
350
+ *
351
+ * @param doc - The document.
352
+ * @returns The JSON text, or `undefined` for a value that is not JSON data.
353
+ */
354
+ export function documentKey(doc: unknown): string | undefined {
355
+ return keyOf(doc, new Set(), false);
356
+ }
357
+
358
+ function keyOf(value: unknown, open: Set<object>, sorted = true): string | undefined {
359
+ if (value === null || typeof value === "boolean" || typeof value === "string") return JSON.stringify(value);
360
+ if (typeof value === "number") return !Number.isFinite(value) ? undefined : Object.is(value, -0) ? "-0" : JSON.stringify(value);
361
+ if (typeof value !== "object" || open.has(value) || (!Array.isArray(value) && !isRecord(value))) return undefined;
362
+ open.add(value);
363
+ try {
364
+ const list = Array.isArray(value);
365
+ const names = list ? [] : sorted ? Object.keys(value).sort() : Object.keys(value);
366
+ const items = list ? value.map((item) => keyOf(item, open, sorted)) : names.map((name) => keyOf(field(value as JsonRecord, name), open, sorted));
367
+ if (items.includes(undefined)) return undefined;
368
+ return list ? `[${items.join(",")}]` : `{${names.map((name, index) => `${JSON.stringify(name)}:${String(items[index])}`).join(",")}}`;
369
+ } finally {
370
+ open.delete(value);
371
+ }
267
372
  }
268
373
 
269
374
  /**