@mark1russell7/coeval 0.20.1 → 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 +379 -236
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1017 -98
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/conform.ts +13 -4
- package/src/deref.ts +32 -2
- package/src/effects.ts +4 -2
- package/src/evaluate.ts +2 -0
- package/src/facts.ts +3 -2
- package/src/formula.ts +53 -10
- package/src/index.ts +10 -0
- package/src/instances.ts +94 -5
- package/src/parts.ts +30 -1
- package/src/providers.ts +209 -29
- package/src/reference.ts +119 -9
- package/src/static.ts +144 -23
- package/src/stdlib.ts +189 -2
- package/src/types.ts +12 -4
- package/src/world.ts +135 -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
|
|
|
@@ -116,12 +116,20 @@ export interface LazyRule {
|
|
|
116
116
|
* - `lazy`: the parts that an evaluation can skip and still give a value.
|
|
117
117
|
* - `reads`: the roots that the code of the kind resolves as a whole, with the path of one root. The static facts
|
|
118
118
|
* give a read of each root at each document of the kind (D44). The tree node reads `self` this way.
|
|
119
|
+
* - `effects`: the effects that the code of `eval` can cause, in the form of the field `effects` of a kind (D27). In
|
|
120
|
+
* the answer of the hook `facts`, it gives the effects of the code of a name. The contract of a kind does not
|
|
121
|
+
* describe an own entry for another evaluator, thus only the hook can give its effects (CAP.OWN-EFFECTS, D118).
|
|
122
|
+
* - `drives`: the parts that the code of a runner evaluates itself, without a wait, in the form of the lazy rules. A
|
|
123
|
+
* step under such a part cannot wait for the host. The check of a run then refuses a part that can wait there
|
|
124
|
+
* (RUN.DRIVES, D119). Without `drives`, the check assumes that the runner passes each wait of its parts through.
|
|
119
125
|
*/
|
|
120
126
|
export interface KindFacts {
|
|
121
127
|
readonly pure?: boolean;
|
|
122
128
|
readonly volatile?: boolean;
|
|
123
129
|
readonly lazy?: readonly LazyRule[];
|
|
124
130
|
readonly reads?: readonly Root[];
|
|
131
|
+
readonly effects?: "type" | readonly string[];
|
|
132
|
+
readonly drives?: readonly LazyRule[];
|
|
125
133
|
}
|
|
126
134
|
|
|
127
135
|
/**
|
|
@@ -136,14 +144,38 @@ export interface KindFacts {
|
|
|
136
144
|
* - `default`: the term that gives the value when no rung before it answers and the declaration has no fold.
|
|
137
145
|
* - `circular`: a cycle of this attribute gives its fixed point, not `#CYCLE!` (TREE.CIRCULAR, D113). Only a cycle
|
|
138
146
|
* whose attributes each declare `circular` gives a fixed point.
|
|
147
|
+
* - `collect`: the value of a node without an answer of the first four rungs and without a fold. The node combines
|
|
148
|
+
* the contributions of each node of its subtree (TREE.COLLECT, D122).
|
|
139
149
|
*/
|
|
140
150
|
export interface AttributeDeclaration {
|
|
141
151
|
readonly copy?: boolean;
|
|
142
152
|
readonly fold?: { readonly unit: Json; readonly combine: Json };
|
|
143
153
|
readonly default?: Json;
|
|
144
154
|
readonly circular?: CircularDeclaration;
|
|
155
|
+
readonly collect?: CollectDeclaration;
|
|
145
156
|
}
|
|
146
157
|
|
|
158
|
+
/**
|
|
159
|
+
* The declaration `collect` of an attribute (TREE.COLLECT, D122). The node of the attribute visits each node of its
|
|
160
|
+
* subtree. It visits the node itself first. Then it visits the nodes in its list `children`, in the order of the list.
|
|
161
|
+
*
|
|
162
|
+
* - `{ unit, combine }`: the collection starts with the term `unit`. The term `combine` then adds the contribution
|
|
163
|
+
* of each node, with the names `acc` and `item`. A node contributes when its kind has an equation for the
|
|
164
|
+
* attribute in `tree.contributes`.
|
|
165
|
+
* - `"names"`: the registry of binding names. Its value is a record from the header name of each node to its node
|
|
166
|
+
* (TREE.NAMED). Two nodes with one name give `#VALUE!` at the read of the registry.
|
|
167
|
+
*/
|
|
168
|
+
export type CollectDeclaration = { readonly unit: Json; readonly combine: Json } | "names";
|
|
169
|
+
|
|
170
|
+
/** A declaration `collect` that the kit can use, or the reason of a bad declaration (D122). */
|
|
171
|
+
export type CollectRule =
|
|
172
|
+
| { readonly ok: true; readonly names: true }
|
|
173
|
+
| { readonly ok: true; readonly names: false; readonly unit: Json; readonly combine: Json }
|
|
174
|
+
| { readonly ok: false; readonly reason: string };
|
|
175
|
+
|
|
176
|
+
/** The rule of the registry of binding names. */
|
|
177
|
+
const NAMES_RULE: CollectRule = /* @__PURE__ */ Object.freeze({ ok: true, names: true });
|
|
178
|
+
|
|
147
179
|
/**
|
|
148
180
|
* The declaration `circular` of an attribute (TREE.CIRCULAR, D113). The attribute that a cycle reaches first leads it.
|
|
149
181
|
* The leader evaluates again until no value of the cycle changes.
|
|
@@ -200,6 +232,21 @@ export function circularOf(declared: unknown): CircularRule {
|
|
|
200
232
|
return { ok: true, start: start as Json, limit };
|
|
201
233
|
}
|
|
202
234
|
|
|
235
|
+
/**
|
|
236
|
+
* This function reads the declaration `collect` of an attribute (D122). The tree kit, the reference, `check` and the
|
|
237
|
+
* graph of attribute instances read it with this one function.
|
|
238
|
+
*
|
|
239
|
+
* @param declared - The value of the field `collect`.
|
|
240
|
+
* @returns The rule of the collection, or the reason when the declaration has no correct form.
|
|
241
|
+
*/
|
|
242
|
+
export function collectOf(declared: unknown): CollectRule {
|
|
243
|
+
if (declared === "names") return NAMES_RULE;
|
|
244
|
+
if (!isRecord(declared) || !Object.hasOwn(declared, "unit") || !Object.hasOwn(declared, "combine")) {
|
|
245
|
+
return { ok: false, reason: 'The declaration collect needs the text "names" or a record with the fields unit and combine.' };
|
|
246
|
+
}
|
|
247
|
+
return { ok: true, names: false, unit: field(declared, "unit") as Json, combine: field(declared, "combine") as Json };
|
|
248
|
+
}
|
|
249
|
+
|
|
203
250
|
/**
|
|
204
251
|
* The tree contract of a kind (D42): the equations of the attributes of its nodes. Each equation is a document that
|
|
205
252
|
* evaluates with the node as `self` and without capabilities. A bare equation gets the type `of` of the parts of the
|
|
@@ -209,11 +256,98 @@ export function circularOf(declared: unknown): CircularRule {
|
|
|
209
256
|
* - `down`: the attributes of each child of the node. `self` is this node. An equation sees the names `child`,
|
|
210
257
|
* `index` and `prev` (TREE.DOWN).
|
|
211
258
|
* - `attributes`: the declarations of the attributes of the node, for the rungs `copy` and `default`.
|
|
259
|
+
* - `contributes`: the contribution of the node to each collection of an attribute (TREE.COLLECT, D122). An
|
|
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.
|
|
212
263
|
*/
|
|
213
264
|
export interface TreeContract {
|
|
214
265
|
readonly equations?: Readonly<Record<string, Json>>;
|
|
215
266
|
readonly down?: Readonly<Record<string, Json>>;
|
|
216
267
|
readonly attributes?: Readonly<Record<string, AttributeDeclaration>>;
|
|
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
|
+
}
|
|
217
351
|
}
|
|
218
352
|
|
|
219
353
|
/**
|