@mark1russell7/coeval 0.18.0 → 0.19.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/README.md +4 -4
- package/build/index.d.ts +263 -20
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2686 -1379
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/codec.ts +30 -10
- package/src/durable.ts +17 -5
- package/src/facts.ts +55 -19
- package/src/formula.ts +45 -1
- package/src/index.ts +16 -2
- package/src/instances.ts +637 -0
- package/src/reads.ts +186 -2
- package/src/static.ts +357 -50
- package/src/stdlib.ts +127 -6
- package/src/types.ts +6 -2
- package/src/typing.ts +143 -3
- package/src/world.ts +49 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@mark1russell7/coeval",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.19.0",
|
|
5
5
|
"description": "coeval: the kernel of declarative languages. Documents in envelopes, a ladder that finds the evaluator, kind contracts, compiled plans, a static kit, a formula kit and a run side with capabilities.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./build/index.js",
|
package/src/codec.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Json, isEnvelope, isRecord } from "./doc.ts";
|
|
2
2
|
import { type Result, describe, fail, ok } from "./errors.ts";
|
|
3
3
|
import { type Machine, makeContext, prepare } from "./evaluate.ts";
|
|
4
|
-
import { type Domain, type DomainCodec, domainOf, member } from "./types.ts";
|
|
4
|
+
import { type Domain, type DomainCodec, domainOf, lineOf, member } from "./types.ts";
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
7
|
* The codec of the values that JSON cannot write (K140, D40). A document holds such a value as a standard extension:
|
|
@@ -71,7 +71,7 @@ function setOwn(target: Record<string, Json>, key: string, value: Json): void {
|
|
|
71
71
|
/** The state of one call of `encodeValue`. */
|
|
72
72
|
interface Encoder {
|
|
73
73
|
readonly domains: Domains;
|
|
74
|
-
/** The domains with a codec, in the order of
|
|
74
|
+
/** The domains with a codec, in the order of `codecOrder`. */
|
|
75
75
|
readonly codecs: readonly (readonly [string, DomainCodec])[];
|
|
76
76
|
/** The answer of `isJson` for each container that it examined. */
|
|
77
77
|
readonly json: WeakMap<object, boolean>;
|
|
@@ -151,7 +151,8 @@ function encodeTerm(value: unknown, encoder: Encoder): Json {
|
|
|
151
151
|
*
|
|
152
152
|
* - A value that JSON writes exactly stays as it is. A literal with the shape of an envelope goes into `lit`.
|
|
153
153
|
* - `NaN`, an infinity and `-0` become `ext.number`, and `undefined` becomes `ext.undefined`.
|
|
154
|
-
* - A member of a domain with a codec becomes `ext.domain`. The
|
|
154
|
+
* - A member of a domain with a codec becomes `ext.domain`. The codec of a domain under another domain comes first
|
|
155
|
+
* (EXT.SPECIFIC, D109). The order of the names decides between unrelated domains.
|
|
155
156
|
* - A list or a plain record with such a value becomes the construct `list` or `record`.
|
|
156
157
|
*
|
|
157
158
|
* @param machine - The machine, or only its domains.
|
|
@@ -161,15 +162,34 @@ function encodeTerm(value: unknown, encoder: Encoder): Json {
|
|
|
161
162
|
*/
|
|
162
163
|
export function encodeValue(machine: Pick<Machine, "domains">, value: unknown): Json {
|
|
163
164
|
const domains = machine.domains;
|
|
165
|
+
return encodeTerm(value, { domains, codecs: codecOrder(domains), json: new WeakMap(), open: new Set() });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* This function gives the domains with a codec in the order of `encodeValue` (EXT.SPECIFIC, D109). The depth of a
|
|
170
|
+
* domain is the count of the other domains with a codec in its line. A deeper domain comes first, and the order of the
|
|
171
|
+
* names decides between domains of the same depth (EXT.TIE). Thus a domain comes before each domain with a codec above it.
|
|
172
|
+
*/
|
|
173
|
+
function codecOrder(domains: Domains): readonly (readonly [string, DomainCodec])[] {
|
|
164
174
|
const codecs: (readonly [string, DomainCodec])[] = [];
|
|
165
|
-
if (domains
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
175
|
+
if (domains === undefined) return codecs;
|
|
176
|
+
let parents = false;
|
|
177
|
+
for (const name of Object.keys(domains).sort()) {
|
|
178
|
+
const domain = domainOf(name, domains);
|
|
179
|
+
const codec = domain === undefined ? undefined : codecOf(domain);
|
|
180
|
+
if (domain === undefined || codec === undefined) continue;
|
|
181
|
+
codecs.push([name, codec]);
|
|
182
|
+
if (Array.isArray(domain.under)) parents = true;
|
|
183
|
+
}
|
|
184
|
+
// Without a parent of a domain with a codec, each depth is zero, thus the order of the names stays.
|
|
185
|
+
if (!parents) return codecs;
|
|
186
|
+
const depth = new Map<string, number>();
|
|
187
|
+
for (const [name] of codecs) {
|
|
188
|
+
const line = lineOf(name, domains);
|
|
189
|
+
depth.set(name, codecs.filter(([other]) => other !== name && line.has(other)).length);
|
|
171
190
|
}
|
|
172
|
-
|
|
191
|
+
// The sort is stable, thus the order of the names stays between domains of the same depth.
|
|
192
|
+
return codecs.sort(([a], [b]) => (depth.get(b) as number) - (depth.get(a) as number));
|
|
173
193
|
}
|
|
174
194
|
|
|
175
195
|
/**
|
package/src/durable.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Context } from "./deref.ts";
|
|
|
2
2
|
import type { Result } from "./errors.ts";
|
|
3
3
|
import { type Machine, type Plan, type PrepareOptions, type Read, prepare } from "./evaluate.ts";
|
|
4
4
|
import { type DocFacts, type FactsOptions, factsOf } from "./facts.ts";
|
|
5
|
-
import { type VerifyOptions, verifyReads } from "./reads.ts";
|
|
5
|
+
import { type PrintOptions, type ReadPrints, type VerifyOptions, printReads, verifyReads } from "./reads.ts";
|
|
6
6
|
|
|
7
7
|
/*
|
|
8
8
|
* Two levels of durability (R4 P7, D30). The plan and the facts of a document depend only on the structure: the
|
|
@@ -42,6 +42,11 @@ export interface DurableOptions {
|
|
|
42
42
|
readonly volatile?: FactsOptions["volatile"];
|
|
43
43
|
/** The rule of the host for two things, as in `verifyReads`. */
|
|
44
44
|
readonly equal?: VerifyOptions["equal"];
|
|
45
|
+
/**
|
|
46
|
+
* The check of changes in place (READS.IN-PLACE, D112). With it, the cache keeps the prints of the reads of each
|
|
47
|
+
* pure document, and a verification compares them. `true` gives the default limit. Without it, the cache makes no prints.
|
|
48
|
+
*/
|
|
49
|
+
readonly inPlace?: true | PrintOptions;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
/**
|
|
@@ -154,10 +159,11 @@ function byDocument<T>(make: (doc: unknown) => T): (doc: unknown) => T {
|
|
|
154
159
|
* and each result (DURABLE.STRUCTURE).
|
|
155
160
|
*
|
|
156
161
|
* The host keeps two promises. In one data epoch, the context of a key gives the same data. The values of a
|
|
157
|
-
* context and the documents do not change in place, as PLAN.CACHE and `verifyReads` say.
|
|
162
|
+
* context and the documents do not change in place, as PLAN.CACHE and `verifyReads` say. The option `inPlace`
|
|
163
|
+
* finds a change in place of a value that a pure document read (READS.IN-PLACE).
|
|
158
164
|
*
|
|
159
165
|
* @param machine - The machine of the first structure epoch.
|
|
160
|
-
* @param options - The type of a bare root value, the verb, the volatile reads
|
|
166
|
+
* @param options - The type of a bare root value, the verb, the volatile reads, the rule of the host and the check of changes in place.
|
|
161
167
|
* @returns The cache.
|
|
162
168
|
*/
|
|
163
169
|
export function durable(machine: Machine, options: DurableOptions = {}): Durable {
|
|
@@ -167,6 +173,9 @@ export function durable(machine: Machine, options: DurableOptions = {}): Durable
|
|
|
167
173
|
};
|
|
168
174
|
const factsOptions: FactsOptions = { ...prepareOptions, ...(options.volatile === undefined ? {} : { volatile: options.volatile }) };
|
|
169
175
|
const verifyOptions: VerifyOptions = options.equal === undefined ? {} : { equal: options.equal };
|
|
176
|
+
const printOptions: PrintOptions | undefined = options.inPlace === true ? {} : options.inPlace;
|
|
177
|
+
// The prints of the reads of each entry, only with the option `inPlace`. Thus an entry keeps its shape (PLAN.LEAN).
|
|
178
|
+
const printsOf = printOptions === undefined ? undefined : new WeakMap<Entry, ReadPrints>();
|
|
170
179
|
const counts = { compiles: 0, facts: 0, evaluations: 0, verifications: 0, reuses: 0 };
|
|
171
180
|
const results = new Map<unknown, Entry>();
|
|
172
181
|
let current = machine;
|
|
@@ -205,7 +214,8 @@ export function durable(machine: Machine, options: DurableOptions = {}): Durable
|
|
|
205
214
|
}
|
|
206
215
|
if (entry.reads !== undefined) {
|
|
207
216
|
counts.verifications += 1;
|
|
208
|
-
|
|
217
|
+
const prints = printsOf?.get(entry);
|
|
218
|
+
if (verifyReads(plan, entry.reads, context, prints === undefined ? verifyOptions : { ...verifyOptions, prints }).same) {
|
|
209
219
|
entry.data = epochs.data;
|
|
210
220
|
counts.reuses += 1;
|
|
211
221
|
return entry.result;
|
|
@@ -217,7 +227,9 @@ export function durable(machine: Machine, options: DurableOptions = {}): Durable
|
|
|
217
227
|
counts.evaluations += 1;
|
|
218
228
|
const result = plan.evaluate(context, reads === undefined ? undefined : { reads });
|
|
219
229
|
// A list that grows by push keeps room for more reads. A copy of a short list is smaller (PLAN.LEAN).
|
|
220
|
-
|
|
230
|
+
const made: Entry = { plan, result, reads: reads === undefined ? undefined : reads.length === 0 ? NO_READS : reads.slice(), data: epochs.data };
|
|
231
|
+
results.set(key, made);
|
|
232
|
+
if (printsOf !== undefined && made.reads !== undefined) printsOf.set(made, printReads(made.reads, printOptions));
|
|
221
233
|
return result;
|
|
222
234
|
},
|
|
223
235
|
changeData() {
|
package/src/facts.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Json, type Key, isEnvelope, isKey } from "./doc.ts";
|
|
2
|
-
import { type Kind, type LazyRule, type Side, type World, equationType, lookupKind } from "./world.ts";
|
|
2
|
+
import { type Kind, type KindFacts, type LazyRule, type Side, type World, equationType, lookupKind } from "./world.ts";
|
|
3
3
|
import { answerOf } from "./ladder.ts";
|
|
4
4
|
import { pathMatches, splitParts } from "./parts.ts";
|
|
5
5
|
import { type Root, ROOTS, isRef } from "./deref.ts";
|
|
@@ -43,9 +43,12 @@ export interface DocFacts {
|
|
|
43
43
|
* here. The roots that a kind declares in `reads` are here too, each one at the place of its document.
|
|
44
44
|
*/
|
|
45
45
|
readonly reads: readonly StaticRead[];
|
|
46
|
-
/** `"run"` when one kind of the document is on the run side (FACTS.SIDE). */
|
|
46
|
+
/** `"run"` when one kind of the document is on the run side for the verb of the facts (FACTS.SIDE, FACTS.VERB-SIDE). */
|
|
47
47
|
readonly side: Side;
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* The kinds of the effects that the contracts declare for the verb of the facts, as texts with dots, each one time
|
|
50
|
+
* (FACTS.EFFECTS, FACTS.VERB-EFFECTS).
|
|
51
|
+
*/
|
|
49
52
|
readonly effects: readonly string[];
|
|
50
53
|
/**
|
|
51
54
|
* `true` when each kind of the document declares `pure`, and the document has no effect and is not volatile. Then
|
|
@@ -63,8 +66,10 @@ export interface FactsOptions {
|
|
|
63
66
|
/** The type of a bare root value. */
|
|
64
67
|
readonly declaredType?: string;
|
|
65
68
|
/**
|
|
66
|
-
* The verb whose evaluators the facts describe, as in `prepare`. The default is `eval`. The
|
|
67
|
-
*
|
|
69
|
+
* The verb whose evaluators the facts describe, as in `prepare`. The default is `eval`. The marks `pure`,
|
|
70
|
+
* `volatile` and `lazy` of a contract describe the code of `eval` only. For another verb, only the hook `facts`
|
|
71
|
+
* describes the code (FACTS.VERB). The side and the effects come from the entry of the verb in `verbEffects`
|
|
72
|
+
* (FACTS.VERB-EFFECTS, D111).
|
|
68
73
|
*/
|
|
69
74
|
readonly verb?: string;
|
|
70
75
|
/**
|
|
@@ -92,7 +97,7 @@ function tight<T>(list: readonly T[]): readonly T[] {
|
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
/** What a document gives to its parts: its own lazy mark, and the lazy rules of its kind for the parts. */
|
|
95
|
-
interface Above {
|
|
100
|
+
export interface Above {
|
|
96
101
|
readonly lazy: boolean;
|
|
97
102
|
readonly rules: readonly LazyRule[] | typeof EACH | undefined;
|
|
98
103
|
/** The position of each part among the parts that a rule `after` covers. It is made at the first need. */
|
|
@@ -136,7 +141,7 @@ function positionOf(above: Above, parent: DocVisit, rule: LazyRule, slot: readon
|
|
|
136
141
|
}
|
|
137
142
|
|
|
138
143
|
/** This function tells if the lazy rules of a document cover one of its parts. */
|
|
139
|
-
function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean {
|
|
144
|
+
export function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean {
|
|
140
145
|
const rules = above.rules;
|
|
141
146
|
if (rules === undefined) return false;
|
|
142
147
|
if (rules === EACH) return true;
|
|
@@ -148,6 +153,32 @@ function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean
|
|
|
148
153
|
return false;
|
|
149
154
|
}
|
|
150
155
|
|
|
156
|
+
/** The code of a document for the facts: the answer of the hook, and the lazy rules that the code gives its parts. */
|
|
157
|
+
export interface CodeFacts {
|
|
158
|
+
/** `true` when the contract of the kind describes the code. */
|
|
159
|
+
readonly described: boolean;
|
|
160
|
+
/** The answer of the hook `facts` of the machine for the code. */
|
|
161
|
+
readonly answer: KindFacts | undefined;
|
|
162
|
+
/** The lazy rules of the parts (FACTS.LAZY). Code that the contract and the hook do not describe makes each part lazy. */
|
|
163
|
+
readonly rules: Above["rules"];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* This function gives the code of a document of a kind for the facts (FACTS.LAZY). `factsOf` and the graph of
|
|
168
|
+
* attribute instances read the lazy rules with this one function (D107).
|
|
169
|
+
*
|
|
170
|
+
* @param machine - The world and the hook `facts`.
|
|
171
|
+
* @param v - The visit of the document.
|
|
172
|
+
* @param kind - The kind of the document.
|
|
173
|
+
* @param verb - The verb whose code the facts describe.
|
|
174
|
+
* @returns The answer of the hook and the lazy rules.
|
|
175
|
+
*/
|
|
176
|
+
export function codeFactsOf(machine: Machine, v: DocVisit, kind: Kind, verb: string): CodeFacts {
|
|
177
|
+
const code = codeOf(machine.world, v.declared ? undefined : v.doc.own, kind, verb, "eval");
|
|
178
|
+
const answer = code.name === undefined ? undefined : machine.facts?.(v.typePath ?? [], code.name);
|
|
179
|
+
return { described: code.described, answer, rules: code.described ? (answer?.lazy ?? kind.lazy) : (answer?.lazy ?? EACH) };
|
|
180
|
+
}
|
|
181
|
+
|
|
151
182
|
/**
|
|
152
183
|
* This function gives the static facts of a document, for a schedule (R4 P2, D24). It walks the document one time,
|
|
153
184
|
* by the contracts of its kinds:
|
|
@@ -157,7 +188,7 @@ function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean
|
|
|
157
188
|
* declares `reads` adds a read of each of its roots at each document of the kind. The first document of a tree
|
|
158
189
|
* kind also gets the lazy reads of the equations of its kind (FACTS.EQUATIONS). A read from `self` or from a name
|
|
159
190
|
* of a rung is not one of them, because it is not free (READS.EQUATION).
|
|
160
|
-
* - `side` and `effects`: from the contract of each kind.
|
|
191
|
+
* - `side` and `effects`: from the contract of each kind, for the verb of the facts.
|
|
161
192
|
* - `pure`: each kind declares `pure`, also in an equation, and the document has no effect and is not volatile.
|
|
162
193
|
* - `volatile`: a kind, an operation or a step document declares `volatile`, or the host marks a read.
|
|
163
194
|
*
|
|
@@ -166,9 +197,11 @@ function partLazy(above: Above, parent: DocVisit, slot: readonly Key[]): boolean
|
|
|
166
197
|
* entry. Thus a document with an own entry for another evaluator is not pure, and each of its parts is lazy. The
|
|
167
198
|
* hook can still answer for that evaluator.
|
|
168
199
|
*
|
|
169
|
-
* The option `verb` names the verb whose code the facts describe (FACTS.VERB).
|
|
170
|
-
* `eval`, thus the rule of an own entry applies to each document for another verb.
|
|
171
|
-
*
|
|
200
|
+
* The option `verb` names the verb whose code the facts describe (FACTS.VERB). The marks of a contract describe the
|
|
201
|
+
* code of `eval`, thus the rule of an own entry applies to each document for another verb. For another verb, the
|
|
202
|
+
* effects are the effects that the entry of the verb in `verbEffects` declares (FACTS.VERB-EFFECTS, D111). A kind is
|
|
203
|
+
* on the run side for that verb when its entry declares an effect (FACTS.VERB-SIDE). A kind without an entry for the
|
|
204
|
+
* verb adds no effect, as in `check` (FACTS.VERB-UNDECLARED). Its code is still not pure without an answer of the hook.
|
|
172
205
|
*
|
|
173
206
|
* A read is one resolution of its path. A reference that the resolution reaches and follows is a part of that read
|
|
174
207
|
* (DEREF.COLLAPSE), not a separate read. The facts depend only on the document and the machine. Thus a host computes
|
|
@@ -220,17 +253,15 @@ export function factsOf(machine: Machine, doc: unknown, options: FactsOptions =
|
|
|
220
253
|
above.set(v, { lazy, rules: undefined });
|
|
221
254
|
return undefined;
|
|
222
255
|
}
|
|
223
|
-
const
|
|
224
|
-
|
|
256
|
+
const { described, answer, rules } = codeFactsOf(machine, v, kind, verb);
|
|
257
|
+
above.set(v, { lazy, rules });
|
|
225
258
|
let roots: readonly Root[] | undefined;
|
|
226
|
-
if (
|
|
259
|
+
if (described) {
|
|
227
260
|
note(answer?.pure ?? kind.pure, answer?.volatile ?? kind.volatile);
|
|
228
|
-
above.set(v, { lazy, rules: answer?.lazy ?? kind.lazy });
|
|
229
261
|
roots = answer?.reads ?? kind.reads;
|
|
230
262
|
} else {
|
|
231
263
|
// The contract of the kind does not describe this code. Only the hook can.
|
|
232
264
|
note(answer?.pure, answer?.volatile);
|
|
233
|
-
above.set(v, { lazy, rules: answer?.lazy ?? EACH });
|
|
234
265
|
roots = answer?.reads;
|
|
235
266
|
}
|
|
236
267
|
// The code of the kind resolves each of these roots as a whole, at the place of its document (FACTS.KIND-READS).
|
|
@@ -239,13 +270,18 @@ export function factsOf(machine: Machine, doc: unknown, options: FactsOptions =
|
|
|
239
270
|
if (node !== undefined && root === "self") continue;
|
|
240
271
|
add({ root, keys: NONE, exact: true, lazy, address: false, path: [root], place: tight(place.place), at: tight(place.at) });
|
|
241
272
|
}
|
|
273
|
+
// check and effectsOf read the declared effects with the same function (D27). The verb of the facts selects the
|
|
274
|
+
// declaration: effects for eval, and the entry of verbEffects for another verb (D49, D111).
|
|
275
|
+
const declared = declaredEffects(v, verb);
|
|
276
|
+
// The field side gives the side of eval. For another verb, a declared effect needs a runner, as in check, thus
|
|
277
|
+
// the run side (FACTS.VERB-SIDE).
|
|
278
|
+
const runs = verb === "eval" ? kind.side === "run" : declared.length > 0;
|
|
242
279
|
// An equation evaluates without capabilities, thus an effect in it never reaches the host. It is still not pure.
|
|
243
|
-
if (
|
|
280
|
+
if (runs) {
|
|
244
281
|
if (node === undefined) side = "run";
|
|
245
282
|
else pure = false;
|
|
246
283
|
}
|
|
247
|
-
|
|
248
|
-
for (const keys of declaredEffects(v)) {
|
|
284
|
+
for (const keys of declared) {
|
|
249
285
|
if (node !== undefined) {
|
|
250
286
|
pure = false;
|
|
251
287
|
continue;
|
package/src/formula.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type EvalError, type Failed, type Result, Failure, fail, ok, thrown } f
|
|
|
5
5
|
import { type Code, type CompileInput, type CompiledEvaluator, type CompiledPart, type Evaluator, type Machine, type Runtime, resolveCompiled, totalCode } from "./evaluate.ts";
|
|
6
6
|
import { type MachinePart, mergeMachines } from "./merge.ts";
|
|
7
7
|
import { type Domain, type ValueType, HEADER_KEYS, STD_DOMAINS, callable, fieldOf, fits, fnOf, holdsEnvelope, isPrecise, isUnknown, itemOf, join, member, stepType, typeIssues, typeText } from "./types.ts";
|
|
8
|
-
import type { PartType, TypeInput, TypeRule } from "./typing.ts";
|
|
8
|
+
import type { Declared, PartType, TypeInput, TypeRule } from "./typing.ts";
|
|
9
9
|
import { decodeDomain, domainTypeOf } from "./codec.ts";
|
|
10
10
|
import { type StaticRead, factsOf } from "./facts.ts";
|
|
11
11
|
|
|
@@ -235,6 +235,12 @@ function isSignature(value: unknown): value is Signature {
|
|
|
235
235
|
return typeof value === "object" && value !== null && Array.isArray((value as { readonly params?: unknown }).params) && Object.hasOwn(value, "to");
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
/** This function tells if two signatures have the same parameters and the same rest parameter. */
|
|
239
|
+
function sameParams(a: Signature, b: Signature): boolean {
|
|
240
|
+
if (!docEquals(a.params, b.params)) return false;
|
|
241
|
+
return a.rest === undefined ? b.rest === undefined : b.rest !== undefined && docEquals(a.rest, b.rest);
|
|
242
|
+
}
|
|
243
|
+
|
|
238
244
|
/** This function gives the text of a signature, for the reason of an issue. */
|
|
239
245
|
function signatureText(sig: Signature): string {
|
|
240
246
|
const params = sig.params.map(typeText);
|
|
@@ -325,6 +331,32 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
|
|
|
325
331
|
}
|
|
326
332
|
return { sigs, bad: sigs.length !== raw.length };
|
|
327
333
|
};
|
|
334
|
+
/**
|
|
335
|
+
* The declarations of the signatures, for `checkWorld` (TYPE.WORLD, D110). Only a record of signatures lists them.
|
|
336
|
+
* An overload with the parameters of an earlier overload is a problem: the earlier one always fits first.
|
|
337
|
+
*/
|
|
338
|
+
const sigsDeclared = (): readonly Declared[] => {
|
|
339
|
+
const sigs = profile.sigs;
|
|
340
|
+
if (sigs === undefined || typeof sigs === "function") return [];
|
|
341
|
+
const out: Declared[] = [];
|
|
342
|
+
for (const name of Object.keys(sigs)) {
|
|
343
|
+
const raw: unknown = sigs[name];
|
|
344
|
+
const list: readonly unknown[] = Array.isArray(raw) ? raw : [raw];
|
|
345
|
+
list.forEach((sig, index) => {
|
|
346
|
+
const at: readonly Key[] = Array.isArray(raw) ? ["sigs", name, index] : ["sigs", name];
|
|
347
|
+
if (!isSignature(sig)) {
|
|
348
|
+
out.push({ at, problem: `A signature of ops.${name} is not a record with params and to. The check does not read it.` });
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
sig.params.forEach((param, position) => out.push({ at: [...at, "params", position], type: param, what: `parameter ${position + 1} of a signature of ops.${name}` }));
|
|
352
|
+
if (sig.rest !== undefined) out.push({ at: [...at, "rest"], type: sig.rest, what: `rest parameter of a signature of ops.${name}` });
|
|
353
|
+
out.push({ at: [...at, "to"], type: sig.to, what: `result of a signature of ops.${name}` });
|
|
354
|
+
const earlier = list.slice(0, index).findIndex((other) => isSignature(other) && sameParams(other, sig));
|
|
355
|
+
if (earlier >= 0) out.push({ at, problem: `The overload ${index + 1} of ops.${name} has the parameters of the overload ${earlier + 1}. Thus the check never selects it.` });
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
return out;
|
|
359
|
+
};
|
|
328
360
|
const logic = (word: "and" | "or"): TypeRule => ({
|
|
329
361
|
part: (input, path) => truthPart(input, `term ${Number(path[0]) + 1} of ${word}`),
|
|
330
362
|
synth: (input) => {
|
|
@@ -456,6 +488,8 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
|
|
|
456
488
|
},
|
|
457
489
|
|
|
458
490
|
"formula.if": {
|
|
491
|
+
// The rule of if declares the truth type of the profile, thus one bad truth type gives one issue in checkWorld.
|
|
492
|
+
declared: () => (declared ? [{ at: ["truthType"], type: truth, what: "truth type of the profile" }] : []),
|
|
459
493
|
part: (input, path) => {
|
|
460
494
|
if (path[0] === "test") return truthPart(input, "test of if");
|
|
461
495
|
return isUnknown(input.expected, input.machine.domains) ? {} : { expect: input.expected, what: `${String(path[0])} branch of if` };
|
|
@@ -471,6 +505,7 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
|
|
|
471
505
|
"formula.or": logic("or"),
|
|
472
506
|
|
|
473
507
|
"formula.op": {
|
|
508
|
+
declared: sigsDeclared,
|
|
474
509
|
part: (input, path) => {
|
|
475
510
|
const read = signatures(input);
|
|
476
511
|
// Only one signature gives the types of the arguments before their check. Overloads select after it.
|
|
@@ -510,6 +545,15 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
|
|
|
510
545
|
},
|
|
511
546
|
|
|
512
547
|
"formula.ext": {
|
|
548
|
+
declared: () => {
|
|
549
|
+
const types = profile.extTypes;
|
|
550
|
+
const own = profile.ext;
|
|
551
|
+
if (types === undefined || own === undefined) return [];
|
|
552
|
+
// Only the type of an own extension counts. An entry for a standard extension has no effect (D103).
|
|
553
|
+
return Object.keys(types)
|
|
554
|
+
.filter((name) => Object.hasOwn(own, name))
|
|
555
|
+
.map((name) => ({ at: ["extTypes", name], type: types[name], what: `type of ext.${name} in extTypes` }));
|
|
556
|
+
},
|
|
513
557
|
synth: (input) => {
|
|
514
558
|
const name = nameOf(input);
|
|
515
559
|
const own = profile.ext !== undefined && Object.hasOwn(profile.ext, name);
|
package/src/index.ts
CHANGED
|
@@ -164,9 +164,12 @@ export {
|
|
|
164
164
|
run,
|
|
165
165
|
} from "./evaluate.ts";
|
|
166
166
|
export {
|
|
167
|
+
type AttributeAnswer,
|
|
168
|
+
type AttributeCell,
|
|
167
169
|
type AttributeRung,
|
|
168
170
|
type AttributeTrace,
|
|
169
171
|
type NearestOptions,
|
|
172
|
+
type TreeOptions,
|
|
170
173
|
ATTRIBUTE_RUNGS,
|
|
171
174
|
STD_CAPS,
|
|
172
175
|
STD_EVALUATORS,
|
|
@@ -178,6 +181,7 @@ export {
|
|
|
178
181
|
attributeTrace,
|
|
179
182
|
nearestWalker,
|
|
180
183
|
placeOf,
|
|
184
|
+
treeNodeEvaluator,
|
|
181
185
|
} from "./stdlib.ts";
|
|
182
186
|
export {
|
|
183
187
|
type Edge,
|
|
@@ -208,6 +212,7 @@ export {
|
|
|
208
212
|
type WalkOptions,
|
|
209
213
|
type WalkProblem,
|
|
210
214
|
type WorldIssue,
|
|
215
|
+
type WorldOptions,
|
|
211
216
|
check,
|
|
212
217
|
checkWorld,
|
|
213
218
|
mapRefs,
|
|
@@ -259,7 +264,7 @@ export { type Definitions, type ReferenceOptions, type ReferenceRunOptions, STD_
|
|
|
259
264
|
export { type AgreeOptions, type AgreeRunOptions, type Agreement, type Tier, type TierInput, type TierResult, agree, agreeRun, sameValue } from "./agree.ts";
|
|
260
265
|
export { type ConformOptions, type ConformProbe, type Finding, type Obligation, conforms } from "./conform.ts";
|
|
261
266
|
export { sameResult } from "./same.ts";
|
|
262
|
-
export { type ReadsCheck, type VerifyOptions, verifyReads } from "./reads.ts";
|
|
267
|
+
export { type PrintOptions, type ReadPrints, type ReadsCheck, type VerifyOptions, printReads, verifyReads } from "./reads.ts";
|
|
263
268
|
export { type Durable, type DurableCounts, type DurableOptions, type Epochs, durable } from "./durable.ts";
|
|
264
269
|
export {
|
|
265
270
|
type NodeEdge,
|
|
@@ -281,6 +286,15 @@ export {
|
|
|
281
286
|
agreeSchedule,
|
|
282
287
|
} from "./meaning.ts";
|
|
283
288
|
export { type GraphCycle, type GraphEdge, type GraphNode, type GraphOptions, type NodeGraph, components, cycles, heights, necessary, nodeLayers } from "./graph.ts";
|
|
289
|
+
export {
|
|
290
|
+
type AttributeCycle,
|
|
291
|
+
type AttributeEdge,
|
|
292
|
+
type AttributeGraph,
|
|
293
|
+
type AttributeGraphOptions,
|
|
294
|
+
type AttributeVertex,
|
|
295
|
+
attributeGraph,
|
|
296
|
+
attributeVertexId,
|
|
297
|
+
} from "./instances.ts";
|
|
284
298
|
export {
|
|
285
299
|
type Recorder,
|
|
286
300
|
type ReplayCausePartsJson,
|
|
@@ -302,5 +316,5 @@ export {
|
|
|
302
316
|
} from "./replay.ts";
|
|
303
317
|
export { type GenerateOptions, type Random, generate, random, typesOf } from "./generate.ts";
|
|
304
318
|
export { type Domain, type DomainCodec, type TypeIssue, type ValueType, STD_DOMAINS, fits, join, member, typeIssues, typeText } from "./types.ts";
|
|
305
|
-
export { type PartType, type RootTypes, type TypeInput, type TypeProblem, type TypeRule, type Typed, CAPS_TYPE } from "./typing.ts";
|
|
319
|
+
export { type Declared, type PartType, type RootTypes, type TypeInput, type TypeProblem, type TypeRule, type Typed, CAPS_TYPE } from "./typing.ts";
|
|
306
320
|
export { type SampleOptions, type TypeReading, typeFromSamples, typeFromSchema } from "./providers.ts";
|