@mark1russell7/coeval 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.d.ts +62 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +421 -35
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/formula.ts +22 -5
- package/src/index.ts +6 -0
- package/src/instances.ts +45 -5
- package/src/reference.ts +68 -8
- package/src/static.ts +70 -9
- package/src/stdlib.ts +103 -2
- package/src/world.ts +85 -1
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
|
|
|
@@ -258,12 +258,96 @@ export function collectOf(declared: unknown): CollectRule {
|
|
|
258
258
|
* - `attributes`: the declarations of the attributes of the node, for the rungs `copy` and `default`.
|
|
259
259
|
* - `contributes`: the contribution of the node to each collection of an attribute (TREE.COLLECT, D122). An
|
|
260
260
|
* equation sees no names.
|
|
261
|
+
* - `at`: the attributes with parameters of the node (TREE.AT, D124). Only the step `tree.at` reads them, and the
|
|
262
|
+
* equation sees each parameter as a name.
|
|
261
263
|
*/
|
|
262
264
|
export interface TreeContract {
|
|
263
265
|
readonly equations?: Readonly<Record<string, Json>>;
|
|
264
266
|
readonly down?: Readonly<Record<string, Json>>;
|
|
265
267
|
readonly attributes?: Readonly<Record<string, AttributeDeclaration>>;
|
|
266
268
|
readonly contributes?: Readonly<Record<string, Json>>;
|
|
269
|
+
readonly at?: Readonly<Record<string, AtEquation>>;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* An attribute with parameters (TREE.AT, D124), as `paramAttr` in Kiama. The step `tree.at` gives the arguments. The
|
|
274
|
+
* equation evaluates with the node as `self`, and it sees each parameter as a name, for example `names.width`.
|
|
275
|
+
*/
|
|
276
|
+
export interface AtEquation {
|
|
277
|
+
/** The names of the parameters. The step gives an argument for each of them, and no other argument. */
|
|
278
|
+
readonly params: readonly string[];
|
|
279
|
+
readonly equation: Json;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** An entry of `tree.at` that the kit can use, or the reason of a bad entry (D124). */
|
|
283
|
+
export type AtRule = { readonly ok: true; readonly params: readonly string[]; readonly equation: Json } | { readonly ok: false; readonly reason: string };
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* This function reads an entry of the table `tree.at` (D124). The tree kit, the reference, `check` and the graph of
|
|
287
|
+
* attribute instances read it with this one function.
|
|
288
|
+
*
|
|
289
|
+
* @param declared - The entry.
|
|
290
|
+
* @returns The rule, or the reason when the entry has no correct form.
|
|
291
|
+
*/
|
|
292
|
+
export function atRuleOf(declared: unknown): AtRule {
|
|
293
|
+
const params = isRecord(declared) ? field(declared, "params") : undefined;
|
|
294
|
+
const good = Array.isArray(params) && params.every((name, index) => typeof name === "string" && params.indexOf(name) === index);
|
|
295
|
+
if (!good || !Object.hasOwn(declared as object, "equation")) {
|
|
296
|
+
return { ok: false, reason: "An entry of tree.at needs a list params of different texts and the field equation." };
|
|
297
|
+
}
|
|
298
|
+
return { ok: true, params: params as readonly string[], equation: field(declared as JsonRecord, "equation") as Json };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** The arguments of a step `tree.at` (D124): the names of the equation and their key, or the reason of bad arguments. */
|
|
302
|
+
export type AtArgs = { readonly ok: true; readonly names: Readonly<Record<string, Json>>; readonly key: string } | { readonly ok: false; readonly reason: string };
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* This function examines the arguments of a step `tree.at` (D124). The step gives an argument for each parameter, and
|
|
306
|
+
* no other argument. Each argument is JSON data. The key is the text of `argsKey`, thus two equal arguments give one key.
|
|
307
|
+
*
|
|
308
|
+
* @param params - The parameters of the attribute.
|
|
309
|
+
* @param args - The field `args` of the step.
|
|
310
|
+
* @returns The names of the equation and the key, or the reason.
|
|
311
|
+
*/
|
|
312
|
+
export function atArgsOf(params: readonly string[], args: unknown): AtArgs {
|
|
313
|
+
if (!isRecord(args)) return { ok: false, reason: "The arguments are a record." };
|
|
314
|
+
const extra = Object.keys(args).find((name) => !params.includes(name));
|
|
315
|
+
if (extra !== undefined) return { ok: false, reason: `The attribute has no parameter ${extra}.` };
|
|
316
|
+
const names: Record<string, Json> = {};
|
|
317
|
+
for (const name of params) {
|
|
318
|
+
if (!Object.hasOwn(args, name)) return { ok: false, reason: `The step gives no argument ${name}.` };
|
|
319
|
+
const value = field(args, name);
|
|
320
|
+
if (argsKey(value) === undefined) return { ok: false, reason: `The argument ${name} is not JSON data.` };
|
|
321
|
+
Object.defineProperty(names, name, { value, enumerable: true, writable: true, configurable: true });
|
|
322
|
+
}
|
|
323
|
+
return { ok: true, names, key: argsKey(names) as string };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* This function gives the key of a JSON value (D124). The fields of a record are in the order of their names. Two
|
|
328
|
+
* values have the same key only when they are equal by DOC.EQUALS, thus `-0` and `0` have different keys.
|
|
329
|
+
*
|
|
330
|
+
* @param value - The value.
|
|
331
|
+
* @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.
|
|
332
|
+
*/
|
|
333
|
+
export function argsKey(value: unknown): string | undefined {
|
|
334
|
+
return keyOf(value, new Set());
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function keyOf(value: unknown, open: Set<object>): string | undefined {
|
|
338
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") return JSON.stringify(value);
|
|
339
|
+
if (typeof value === "number") return !Number.isFinite(value) ? undefined : Object.is(value, -0) ? "-0" : JSON.stringify(value);
|
|
340
|
+
if (typeof value !== "object" || open.has(value) || (!Array.isArray(value) && !isRecord(value))) return undefined;
|
|
341
|
+
open.add(value);
|
|
342
|
+
try {
|
|
343
|
+
const list = Array.isArray(value);
|
|
344
|
+
const names = list ? [] : Object.keys(value).sort();
|
|
345
|
+
const items = list ? value.map((item) => keyOf(item, open)) : names.map((name) => keyOf(field(value as JsonRecord, name), open));
|
|
346
|
+
if (items.includes(undefined)) return undefined;
|
|
347
|
+
return list ? `[${items.join(",")}]` : `{${names.map((name, index) => `${JSON.stringify(name)}:${String(items[index])}`).join(",")}}`;
|
|
348
|
+
} finally {
|
|
349
|
+
open.delete(value);
|
|
350
|
+
}
|
|
267
351
|
}
|
|
268
352
|
|
|
269
353
|
/**
|