@mark1russell7/coeval 0.20.0 → 0.20.1
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 +1 -0
- package/build/index.d.ts +103 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +138 -1
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/steps.ts +181 -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.20.
|
|
4
|
+
"version": "0.20.1",
|
|
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/index.ts
CHANGED
|
@@ -92,6 +92,7 @@ export {
|
|
|
92
92
|
kindNamed,
|
|
93
93
|
kindOf,
|
|
94
94
|
} from "./ladder.ts";
|
|
95
|
+
export { type StepKey, type StepLadder, type StepNeed, type StepReason, type StepRefusal, type StepWalk, checkStep, isStep, stepLadder, stepOf, stepRank } from "./steps.ts";
|
|
95
96
|
export {
|
|
96
97
|
type Cap,
|
|
97
98
|
type Context,
|
package/src/steps.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The steps of a language (STEPS.*). A step is a level of the power of a language. A low step accepts only programs
|
|
3
|
+
* that end for a known reason, for example loops with a fixed count. A high step accepts more programs. The language
|
|
4
|
+
* declares the step of each construct as data. `stepOf` gives the lowest step that a document needs, with the reason,
|
|
5
|
+
* and `checkStep` compares that step with the highest step that the host accepts. A document cannot raise its own
|
|
6
|
+
* step, because the declaration and the limit come from the language and the host. The ladder of `ask` (`ladder.ts`)
|
|
7
|
+
* is a different thing: it finds the evaluator of a verb.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** A key of a path from the root to a node: a field name or a position. */
|
|
11
|
+
export type StepKey = string | number;
|
|
12
|
+
|
|
13
|
+
/** The steps of a language, from the lowest to the highest, and the step that each construct needs. It is data. */
|
|
14
|
+
export interface StepLadder<S extends string = string> {
|
|
15
|
+
/** The names of the steps, from the lowest to the highest. */
|
|
16
|
+
readonly steps: readonly S[];
|
|
17
|
+
/** The step that each construct needs. A construct without an entry needs the lowest step. */
|
|
18
|
+
readonly constructs: Readonly<Record<string, S>>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The walk of the documents of a language. The language gives these two functions. */
|
|
22
|
+
export interface StepWalk<N> {
|
|
23
|
+
/** This function gives the name of the construct of a node, or `undefined` for a node without a construct. */
|
|
24
|
+
readonly construct: (node: N) => string | undefined;
|
|
25
|
+
/** This function gives the child nodes of a node in document order, each with the key of its path. */
|
|
26
|
+
readonly children: (node: N) => Iterable<readonly [StepKey, N]>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The reason for a step: the first node in document order whose construct needs that step. */
|
|
30
|
+
export interface StepReason<N, S extends string = string> {
|
|
31
|
+
/** The name of the construct. */
|
|
32
|
+
readonly construct: string;
|
|
33
|
+
/** The step that the construct needs. */
|
|
34
|
+
readonly step: S;
|
|
35
|
+
/** The keys from the root to the node. The root has the empty path. */
|
|
36
|
+
readonly path: readonly StepKey[];
|
|
37
|
+
readonly node: N;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The lowest step that a document needs. */
|
|
41
|
+
export interface StepNeed<N, S extends string = string> {
|
|
42
|
+
readonly step: S;
|
|
43
|
+
/** The reason. It is `undefined` when no construct needs more than the lowest step. */
|
|
44
|
+
readonly reason?: StepReason<N, S>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The refusal of a document whose step is higher than the limit of the host. */
|
|
48
|
+
export interface StepRefusal<N, S extends string = string> {
|
|
49
|
+
/** The step that the document needs. */
|
|
50
|
+
readonly needed: S;
|
|
51
|
+
/** The highest step that the host accepts. */
|
|
52
|
+
readonly maxStep: S;
|
|
53
|
+
/** The reason of the step. A need that a host makes without `stepOf` can have no reason. */
|
|
54
|
+
readonly reason?: StepReason<N, S>;
|
|
55
|
+
/** The text of the refusal. It names the needed step and the limit, and the construct and its path of the reason. */
|
|
56
|
+
readonly message: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* This function makes a ladder of steps, and it checks the declaration (STEPS.LADDER). The list of steps must have one
|
|
61
|
+
* step or more, with no name two times. The step of each construct must be a step of the list.
|
|
62
|
+
*
|
|
63
|
+
* @param steps - The names of the steps, from the lowest to the highest.
|
|
64
|
+
* @param constructs - The step that each construct needs.
|
|
65
|
+
* @returns The ladder, frozen.
|
|
66
|
+
* @throws TypeError for a declaration that is not valid, because it is a defect of the language.
|
|
67
|
+
*/
|
|
68
|
+
export function stepLadder<const S extends string>(steps: readonly S[], constructs: Readonly<Record<string, NoInfer<S>>>): StepLadder<S> {
|
|
69
|
+
if (steps.length === 0) throw new TypeError("a ladder of steps needs one step or more");
|
|
70
|
+
const seen = new Set<string>();
|
|
71
|
+
for (const step of steps) {
|
|
72
|
+
if (typeof step !== "string") throw new TypeError(`the step ${String(step)} is not a name`);
|
|
73
|
+
if (seen.has(step)) throw new TypeError(`the step "${step}" is in the ladder two times`);
|
|
74
|
+
seen.add(step);
|
|
75
|
+
}
|
|
76
|
+
for (const [construct, step] of Object.entries(constructs)) {
|
|
77
|
+
if (!seen.has(step)) throw new TypeError(`the construct "${construct}" needs the step "${String(step)}", which is not a step of the ladder`);
|
|
78
|
+
}
|
|
79
|
+
return Object.freeze({ steps: Object.freeze([...steps]), constructs: Object.freeze({ ...constructs }) });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* This function tells if a value is a step of a ladder. A host uses it to check its option for the highest step.
|
|
84
|
+
*
|
|
85
|
+
* @param ladder - The ladder.
|
|
86
|
+
* @param value - The value.
|
|
87
|
+
* @returns `true` when the value is the name of a step of the ladder.
|
|
88
|
+
*/
|
|
89
|
+
export function isStep<S extends string>(ladder: StepLadder<S>, value: unknown): value is S {
|
|
90
|
+
return typeof value === "string" && ladder.steps.includes(value as S);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* This function gives the position of a step in its ladder. The lowest step has the position 0.
|
|
95
|
+
*
|
|
96
|
+
* @param ladder - The ladder.
|
|
97
|
+
* @param step - The step.
|
|
98
|
+
* @returns The position.
|
|
99
|
+
* @throws TypeError for a name that is not a step of the ladder.
|
|
100
|
+
*/
|
|
101
|
+
export function stepRank<S extends string>(ladder: StepLadder<S>, step: S): number {
|
|
102
|
+
const rank = ladder.steps.indexOf(step);
|
|
103
|
+
if (rank < 0) throw new TypeError(`"${String(step)}" is not a step of the ladder. The steps are ${ladder.steps.join(", ")}`);
|
|
104
|
+
return rank;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** An item of the walk: a node, the key of its path and the item of its parent. Thus a path costs nothing until a reason needs it. */
|
|
108
|
+
interface Visit<N> {
|
|
109
|
+
readonly node: N;
|
|
110
|
+
readonly key: StepKey | undefined;
|
|
111
|
+
readonly parent: Visit<N> | undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function pathOf<N>(visit: Visit<N>): StepKey[] {
|
|
115
|
+
const path: StepKey[] = [];
|
|
116
|
+
for (let v: Visit<N> | undefined = visit; v?.key !== undefined; v = v.parent) path.push(v.key);
|
|
117
|
+
return path.reverse();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* This function gives the lowest step that a document needs, with the reason (STEPS.LOWEST, STEPS.REASON). The step is
|
|
122
|
+
* the highest step of the constructs of its nodes. The reason is the first node in document order with that step. The
|
|
123
|
+
* walk uses its own stack, thus a deep document does not overflow (STEPS.LINEAR). It visits a shared node one time,
|
|
124
|
+
* and it stops at the first node with the highest step of the ladder.
|
|
125
|
+
*
|
|
126
|
+
* @param ladder - The ladder of the language.
|
|
127
|
+
* @param walk - The construct and the child nodes of a node.
|
|
128
|
+
* @param root - The root node of the document.
|
|
129
|
+
* @returns The step, and the reason when the step is not the lowest step.
|
|
130
|
+
*/
|
|
131
|
+
export function stepOf<N, S extends string>(ladder: StepLadder<S>, walk: StepWalk<N>, root: N): StepNeed<N, S> {
|
|
132
|
+
const top = ladder.steps.length - 1;
|
|
133
|
+
let best = 0;
|
|
134
|
+
let reason: StepReason<N, S> | undefined;
|
|
135
|
+
const seen = new Set<unknown>();
|
|
136
|
+
const todo: Visit<N>[] = [{ node: root, key: undefined, parent: undefined }];
|
|
137
|
+
for (let visit = todo.pop(); visit !== undefined; visit = todo.pop()) {
|
|
138
|
+
const { node } = visit;
|
|
139
|
+
if (typeof node === "object" && node !== null) {
|
|
140
|
+
if (seen.has(node)) continue;
|
|
141
|
+
seen.add(node);
|
|
142
|
+
}
|
|
143
|
+
const construct = walk.construct(node);
|
|
144
|
+
const step = construct === undefined || !Object.hasOwn(ladder.constructs, construct) ? undefined : ladder.constructs[construct];
|
|
145
|
+
const rank = step === undefined ? 0 : ladder.steps.indexOf(step);
|
|
146
|
+
if (rank > best) {
|
|
147
|
+
best = rank;
|
|
148
|
+
reason = { construct: construct as string, step: step as S, path: pathOf(visit), node };
|
|
149
|
+
if (best === top) break;
|
|
150
|
+
}
|
|
151
|
+
const kids = [...walk.children(node)];
|
|
152
|
+
for (let i = kids.length - 1; i >= 0; i--) {
|
|
153
|
+
const [key, child] = kids[i] as readonly [StepKey, N];
|
|
154
|
+
todo.push({ node: child, key, parent: visit });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const step = ladder.steps[best] as S;
|
|
158
|
+
return reason === undefined ? { step } : { step, reason };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* This function compares the step of a document with the highest step that the host accepts (STEPS.MAX). The limit
|
|
163
|
+
* changes only which documents the host accepts. The language evaluates an accepted document as before, thus a limit
|
|
164
|
+
* does not change a value.
|
|
165
|
+
*
|
|
166
|
+
* @param ladder - The ladder of the language.
|
|
167
|
+
* @param need - The step of the document, from `stepOf`.
|
|
168
|
+
* @param maxStep - The highest step that the host accepts.
|
|
169
|
+
* @returns `undefined` when the host accepts the document, or the refusal that names the needed step.
|
|
170
|
+
* @throws TypeError when `maxStep` or the step of `need` is not a step of the ladder.
|
|
171
|
+
*/
|
|
172
|
+
export function checkStep<N, S extends string>(ladder: StepLadder<S>, need: StepNeed<N, S>, maxStep: S): StepRefusal<N, S> | undefined {
|
|
173
|
+
const limit = stepRank(ladder, maxStep);
|
|
174
|
+
const needed = stepRank(ladder, need.step);
|
|
175
|
+
if (needed <= limit) return undefined;
|
|
176
|
+
const head = `the document needs the step ${need.step}, and the host accepts the steps up to ${maxStep}`;
|
|
177
|
+
const reason = need.reason;
|
|
178
|
+
if (reason === undefined) return { needed: need.step, maxStep, message: head };
|
|
179
|
+
const where = reason.path.length === 0 ? "at the root" : `at the path ${JSON.stringify(reason.path)}`;
|
|
180
|
+
return { needed: need.step, maxStep, reason, message: `${head}. The construct "${reason.construct}" ${where} needs the step ${reason.step}` };
|
|
181
|
+
}
|