@mark1russell7/coeval 0.6.3 → 0.7.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 +63 -5
- package/build/index.d.ts +1771 -254
- package/build/index.d.ts.map +1 -1
- package/build/index.js +5809 -511
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/agree.ts +15 -3
- package/src/codec.ts +185 -0
- package/src/conform.ts +1068 -0
- package/src/deref.ts +284 -47
- package/src/durable.ts +238 -0
- package/src/effects.ts +110 -0
- package/src/errors.ts +2 -1
- package/src/evaluate.ts +480 -113
- package/src/facts.ts +252 -0
- package/src/formula.ts +450 -42
- package/src/graph.ts +399 -0
- package/src/index.ts +46 -0
- package/src/ladder.ts +40 -1
- package/src/merge.ts +91 -4
- package/src/parts.ts +26 -11
- package/src/providers.ts +286 -0
- package/src/reads.ts +105 -0
- package/src/reference.ts +23 -9
- package/src/schedule.ts +436 -0
- package/src/static.ts +266 -29
- package/src/stdlib.ts +24 -7
- package/src/syntax.ts +846 -0
- package/src/types.ts +768 -0
- package/src/typing.ts +390 -0
- package/src/world.ts +63 -9
package/src/graph.ts
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import type { Side } from "./world.ts";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The analysis of a graph of nodes (S2, R4 P5, D33). A host gives each node with its edges: the nodes that it reads.
|
|
5
|
+
* The analysis gives the strongly connected components, the heights of a push schedule, the layers of independent
|
|
6
|
+
* nodes, the nodes that an observer needs and the static cycles. Each function walks the graph with its own stack,
|
|
7
|
+
* not with the stack of the host. Thus a chain of 100000 nodes does not overflow (GRAPH.SCC).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** One edge of a graph of nodes: a read of a node by another node. `NodeEdge` of a schedule is such an edge. */
|
|
11
|
+
export interface GraphEdge<Id> {
|
|
12
|
+
/** The node that the read reaches. It is `undefined` when the read can reach each node (GRAPH.UNKNOWN). */
|
|
13
|
+
readonly to: Id | undefined;
|
|
14
|
+
/** `true` when only a lazy part reaches the read. An evaluation can then give a value without it (FACTS.LAZY). */
|
|
15
|
+
readonly lazy: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** One node of a graph, with its edges. `NodeInfo` of a schedule is such a node. */
|
|
19
|
+
export interface GraphNode<Id> {
|
|
20
|
+
/** The edges to the nodes that the node reads. */
|
|
21
|
+
readonly edges: readonly GraphEdge<Id>[];
|
|
22
|
+
/** The side of the node. A node on the run side is a sink: no edge reaches it (SCHED.SIDE). The default is `derive`. */
|
|
23
|
+
readonly side?: Side;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A graph of nodes, by name. The order of the map is the order of the nodes in each answer. The function `graph` of
|
|
28
|
+
* a schedule gives such a graph. An edge to a name that is not in the map, or to a sink, is not an edge of the
|
|
29
|
+
* analysis.
|
|
30
|
+
*/
|
|
31
|
+
export type NodeGraph<Id> = ReadonlyMap<Id, GraphNode<Id>>;
|
|
32
|
+
|
|
33
|
+
/** The options of an analysis. */
|
|
34
|
+
export interface GraphOptions {
|
|
35
|
+
/**
|
|
36
|
+
* `true`: the analysis keeps only the eager edges with a target node. Such a read occurs in each evaluation in
|
|
37
|
+
* which each document around it gives a value (FACTS.LAZY). The default keeps each edge.
|
|
38
|
+
*/
|
|
39
|
+
readonly eager?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One static cycle of a graph (GRAPH.CYCLE). */
|
|
43
|
+
export interface GraphCycle<Id> {
|
|
44
|
+
/** The nodes of the cycle, in the order of the graph. */
|
|
45
|
+
readonly nodes: readonly Id[];
|
|
46
|
+
/**
|
|
47
|
+
* `true` when the eager edges with a target close the cycle. `false` when the cycle needs a lazy edge or an edge
|
|
48
|
+
* without a target. The value of each node still comes from the demand (D31).
|
|
49
|
+
*/
|
|
50
|
+
readonly certain: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A graph with a number for each node, and its edges in arrays. The edges of the node `v` are the items of `targets`
|
|
55
|
+
* from `start[v]` up to `start[v + 1]`. An edge without a target goes to the extra node `all`, and `all` reaches each
|
|
56
|
+
* node that is not a sink. Thus such an edge costs one edge, not one edge for each node.
|
|
57
|
+
*/
|
|
58
|
+
interface Indexed<Id> {
|
|
59
|
+
readonly ids: readonly Id[];
|
|
60
|
+
/** The count of the nodes, with the node `all` when the graph has it. */
|
|
61
|
+
readonly size: number;
|
|
62
|
+
readonly start: Int32Array;
|
|
63
|
+
readonly targets: Int32Array;
|
|
64
|
+
/** The number of the node `all`, or `-1` when no edge goes to it. */
|
|
65
|
+
readonly all: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** This function gives the indexed form of a graph, with the edges that the options keep. */
|
|
69
|
+
function indexed<Id>(graph: NodeGraph<Id>, options: GraphOptions): Indexed<Id> {
|
|
70
|
+
const eager = options.eager === true;
|
|
71
|
+
const ids = [...graph.keys()];
|
|
72
|
+
const count = ids.length;
|
|
73
|
+
const numbers = new Map<Id, number>();
|
|
74
|
+
const sink = new Uint8Array(count);
|
|
75
|
+
ids.forEach((id, index) => {
|
|
76
|
+
numbers.set(id, index);
|
|
77
|
+
if (graph.get(id)?.side === "run") sink[index] = 1;
|
|
78
|
+
});
|
|
79
|
+
const start = new Int32Array(count + 2);
|
|
80
|
+
const targets: number[] = [];
|
|
81
|
+
let unknown = false;
|
|
82
|
+
let from = 0;
|
|
83
|
+
for (const node of graph.values()) {
|
|
84
|
+
start[from] = targets.length;
|
|
85
|
+
for (const edge of node.edges) {
|
|
86
|
+
if (eager && edge.lazy) continue;
|
|
87
|
+
if (edge.to === undefined) {
|
|
88
|
+
if (eager) continue;
|
|
89
|
+
unknown = true;
|
|
90
|
+
targets.push(count);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const to = numbers.get(edge.to);
|
|
94
|
+
if (to !== undefined && sink[to] === 0) targets.push(to);
|
|
95
|
+
}
|
|
96
|
+
from += 1;
|
|
97
|
+
}
|
|
98
|
+
start[count] = targets.length;
|
|
99
|
+
if (unknown) for (let to = 0; to < count; to += 1) if (sink[to] === 0) targets.push(to);
|
|
100
|
+
start[count + 1] = targets.length;
|
|
101
|
+
return { ids, size: unknown ? count + 1 : count, start, targets: Int32Array.from(targets), all: unknown ? count : -1 };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The strongly connected components of an indexed graph. */
|
|
105
|
+
interface Strong {
|
|
106
|
+
/** The number of the component of each node. */
|
|
107
|
+
readonly of: Int32Array;
|
|
108
|
+
/** The nodes of each component. Each component comes after each component that it reaches. */
|
|
109
|
+
readonly list: readonly (readonly number[])[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* This function finds the strongly connected components with the algorithm of Tarjan. It keeps its own stack of
|
|
114
|
+
* calls in two arrays, thus a long chain does not overflow the stack of the host. Each component comes after each
|
|
115
|
+
* component that its nodes reach: the deepest component comes first.
|
|
116
|
+
*/
|
|
117
|
+
function strong(graph: Indexed<unknown>): Strong {
|
|
118
|
+
const { size, start, targets } = graph;
|
|
119
|
+
const order = new Int32Array(size).fill(-1);
|
|
120
|
+
const low = new Int32Array(size);
|
|
121
|
+
const of = new Int32Array(size).fill(-1);
|
|
122
|
+
// The nodes of the components that are still open.
|
|
123
|
+
const open = new Int32Array(size);
|
|
124
|
+
const onOpen = new Uint8Array(size);
|
|
125
|
+
// The stack of calls: a node, and the position of its next edge.
|
|
126
|
+
const calls = new Int32Array(size);
|
|
127
|
+
const next = new Int32Array(size);
|
|
128
|
+
const list: number[][] = [];
|
|
129
|
+
let top = 0;
|
|
130
|
+
let count = 0;
|
|
131
|
+
const enter = (node: number, depth: number): void => {
|
|
132
|
+
order[node] = count;
|
|
133
|
+
low[node] = count;
|
|
134
|
+
count += 1;
|
|
135
|
+
open[top] = node;
|
|
136
|
+
top += 1;
|
|
137
|
+
onOpen[node] = 1;
|
|
138
|
+
calls[depth] = node;
|
|
139
|
+
next[depth] = start[node] as number;
|
|
140
|
+
};
|
|
141
|
+
for (let root = 0; root < size; root += 1) {
|
|
142
|
+
if (order[root] !== -1) continue;
|
|
143
|
+
enter(root, 0);
|
|
144
|
+
let depth = 1;
|
|
145
|
+
while (depth > 0) {
|
|
146
|
+
const node = calls[depth - 1] as number;
|
|
147
|
+
const edge = next[depth - 1] as number;
|
|
148
|
+
if (edge < (start[node + 1] as number)) {
|
|
149
|
+
next[depth - 1] = edge + 1;
|
|
150
|
+
const to = targets[edge] as number;
|
|
151
|
+
if (order[to] === -1) {
|
|
152
|
+
enter(to, depth);
|
|
153
|
+
depth += 1;
|
|
154
|
+
} else if (onOpen[to] === 1 && (order[to] as number) < (low[node] as number)) {
|
|
155
|
+
low[node] = order[to] as number;
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
depth -= 1;
|
|
160
|
+
if (low[node] === order[node]) {
|
|
161
|
+
const members: number[] = [];
|
|
162
|
+
let member: number;
|
|
163
|
+
do {
|
|
164
|
+
top -= 1;
|
|
165
|
+
member = open[top] as number;
|
|
166
|
+
onOpen[member] = 0;
|
|
167
|
+
of[member] = list.length;
|
|
168
|
+
members.push(member);
|
|
169
|
+
} while (member !== node);
|
|
170
|
+
list.push(members.sort((a, b) => a - b));
|
|
171
|
+
}
|
|
172
|
+
if (depth > 0) {
|
|
173
|
+
const reader = calls[depth - 1] as number;
|
|
174
|
+
if ((low[node] as number) < (low[reader] as number)) low[reader] = low[node] as number;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { of, list };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** This function tells if a component has a cycle: two or more nodes, or one node with an edge to itself. */
|
|
182
|
+
function cyclic(graph: Indexed<unknown>, members: readonly number[]): boolean {
|
|
183
|
+
if (members.length > 1) return true;
|
|
184
|
+
const node = members[0] as number;
|
|
185
|
+
for (let edge = graph.start[node] as number; edge < (graph.start[node + 1] as number); edge += 1) if (graph.targets[edge] === node) return true;
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** This function gives the names of the nodes of a component, without the node `all`. */
|
|
190
|
+
function named<Id>(graph: Indexed<Id>, members: readonly number[]): Id[] {
|
|
191
|
+
const out: Id[] = [];
|
|
192
|
+
for (const member of members) if (member !== graph.all) out.push(graph.ids[member] as Id);
|
|
193
|
+
return out;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** This function gives the height of each component, in the order of `strong`. The node `all` adds no height. */
|
|
197
|
+
function heightsOf(graph: Indexed<unknown>, found: Strong): Int32Array {
|
|
198
|
+
const { start, targets } = graph;
|
|
199
|
+
const heights = new Int32Array(found.list.length);
|
|
200
|
+
found.list.forEach((members, component) => {
|
|
201
|
+
let highest = -1;
|
|
202
|
+
let real = false;
|
|
203
|
+
for (const node of members) {
|
|
204
|
+
if (node !== graph.all) real = true;
|
|
205
|
+
for (let edge = start[node] as number; edge < (start[node + 1] as number); edge += 1) {
|
|
206
|
+
const other = found.of[targets[edge] as number] as number;
|
|
207
|
+
if (other !== component && (heights[other] as number) > highest) highest = heights[other] as number;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
heights[component] = real ? highest + 1 : highest;
|
|
211
|
+
});
|
|
212
|
+
return heights;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* This function gives the strongly connected components of a graph of nodes (GRAPH.SCC), with the algorithm of
|
|
217
|
+
* Tarjan. It keeps its own stack, thus a chain of 100000 nodes does not overflow the stack of the host.
|
|
218
|
+
*
|
|
219
|
+
* - Each component comes after each component that it reads: the deepest component comes first.
|
|
220
|
+
* - A node that is on no cycle is a component of one node.
|
|
221
|
+
* - The nodes of a component are in the order of the graph.
|
|
222
|
+
*
|
|
223
|
+
* @param graph - The nodes, each with its edges.
|
|
224
|
+
* @param options - The option `eager` keeps only the eager edges with a target node.
|
|
225
|
+
* @returns The components, deepest first.
|
|
226
|
+
*/
|
|
227
|
+
export function components<Id>(graph: NodeGraph<Id>, options: GraphOptions = {}): readonly (readonly Id[])[] {
|
|
228
|
+
const index = indexed(graph, options);
|
|
229
|
+
const out: (readonly Id[])[] = [];
|
|
230
|
+
for (const members of strong(index).list) {
|
|
231
|
+
const names = named(index, members);
|
|
232
|
+
if (names.length > 0) out.push(names);
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* This function gives the height of each node: the order of a push schedule (GRAPH.ORDER). A schedule that evaluates
|
|
239
|
+
* the nodes from the lowest height up evaluates each node after each node that it reads, except on a cycle.
|
|
240
|
+
*
|
|
241
|
+
* - A node that reads no node outside its component has the height 0.
|
|
242
|
+
* - Each other node is one higher than the highest node that it reads outside its component.
|
|
243
|
+
* - The nodes of one component have one height.
|
|
244
|
+
* - An edge without a target reads each node that is not a sink (GRAPH.UNKNOWN).
|
|
245
|
+
*
|
|
246
|
+
* @param graph - The nodes, each with its edges.
|
|
247
|
+
* @param options - The option `eager` keeps only the eager edges with a target node.
|
|
248
|
+
* @returns The height of each node, in the order of the graph.
|
|
249
|
+
*/
|
|
250
|
+
export function heights<Id>(graph: NodeGraph<Id>, options: GraphOptions = {}): ReadonlyMap<Id, number> {
|
|
251
|
+
const index = indexed(graph, options);
|
|
252
|
+
const found = strong(index);
|
|
253
|
+
const levels = heightsOf(index, found);
|
|
254
|
+
const out = new Map<Id, number>();
|
|
255
|
+
index.ids.forEach((id, node) => out.set(id, levels[found.of[node] as number] as number));
|
|
256
|
+
return out;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* This function puts the nodes in layers by their heights, in linear time (GRAPH.LAYERS). The first layer has the
|
|
261
|
+
* height 0. Two nodes of one layer read each other only inside one component. Thus a push schedule can evaluate the
|
|
262
|
+
* components of one layer at the same time.
|
|
263
|
+
*
|
|
264
|
+
* @param graph - The nodes, each with its edges.
|
|
265
|
+
* @param options - The option `eager` keeps only the eager edges with a target node.
|
|
266
|
+
* @returns The layers, from the height 0 up. Each layer keeps the order of the graph.
|
|
267
|
+
*/
|
|
268
|
+
export function nodeLayers<Id>(graph: NodeGraph<Id>, options: GraphOptions = {}): readonly (readonly Id[])[] {
|
|
269
|
+
const out: Id[][] = [];
|
|
270
|
+
for (const [id, height] of heights(graph, options)) {
|
|
271
|
+
while (out.length <= height) out.push([]);
|
|
272
|
+
out[height]?.push(id);
|
|
273
|
+
}
|
|
274
|
+
return out;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* This function gives the nodes that a set of observed nodes needs (GRAPH.NECESSARY). These are the observed nodes,
|
|
279
|
+
* and each node with a path of edges from them. A push schedule evaluates only these nodes. An observed name that is
|
|
280
|
+
* not a node of the graph adds nothing.
|
|
281
|
+
*
|
|
282
|
+
* @param graph - The nodes, each with its edges.
|
|
283
|
+
* @param observed - The nodes that the host shows or reads.
|
|
284
|
+
* @param options - The option `eager` keeps only the eager edges with a target node.
|
|
285
|
+
* @returns The necessary nodes, in the order of the graph.
|
|
286
|
+
*/
|
|
287
|
+
export function necessary<Id>(graph: NodeGraph<Id>, observed: Iterable<Id>, options: GraphOptions = {}): ReadonlySet<Id> {
|
|
288
|
+
const index = indexed(graph, options);
|
|
289
|
+
const numbers = new Map<Id, number>();
|
|
290
|
+
index.ids.forEach((id, node) => numbers.set(id, node));
|
|
291
|
+
const seen = new Uint8Array(index.size);
|
|
292
|
+
const work: number[] = [];
|
|
293
|
+
for (const id of observed) {
|
|
294
|
+
const node = numbers.get(id);
|
|
295
|
+
if (node === undefined || seen[node] === 1) continue;
|
|
296
|
+
seen[node] = 1;
|
|
297
|
+
work.push(node);
|
|
298
|
+
}
|
|
299
|
+
while (work.length > 0) {
|
|
300
|
+
const node = work.pop() as number;
|
|
301
|
+
for (let edge = index.start[node] as number; edge < (index.start[node + 1] as number); edge += 1) {
|
|
302
|
+
const to = index.targets[edge] as number;
|
|
303
|
+
if (seen[to] === 1) continue;
|
|
304
|
+
seen[to] = 1;
|
|
305
|
+
work.push(to);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const out = new Set<Id>();
|
|
309
|
+
index.ids.forEach((id, node) => {
|
|
310
|
+
if (seen[node] === 1) out.add(id);
|
|
311
|
+
});
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* This function gives the static cycles of a graph (GRAPH.CYCLE): each component with two or more nodes, and each
|
|
317
|
+
* node with an edge to itself. A cycle of the eager edges with a target is certain, and `check` or a host can report
|
|
318
|
+
* it before a run. Another cycle is possible: an evaluation can skip a lazy read.
|
|
319
|
+
*
|
|
320
|
+
* - A component of all the edges that is also a component of the eager edges is one certain cycle.
|
|
321
|
+
* - Each other component of all the edges is one possible cycle, and each certain cycle in it comes after it.
|
|
322
|
+
* - A read can still fail before it occurs. Thus the demand of D31, not a static cycle, gives the value of each node.
|
|
323
|
+
*
|
|
324
|
+
* @param graph - The nodes, each with its edges.
|
|
325
|
+
* @returns The cycles, deepest first.
|
|
326
|
+
*/
|
|
327
|
+
export function cycles<Id>(graph: NodeGraph<Id>): readonly GraphCycle<Id>[] {
|
|
328
|
+
const every = indexed(graph, {});
|
|
329
|
+
const found = strong(every);
|
|
330
|
+
const eager = indexed(graph, { eager: true });
|
|
331
|
+
// The certain cycles, by the component of all the edges that holds them.
|
|
332
|
+
const inside = new Map<number, (readonly number[])[]>();
|
|
333
|
+
for (const members of strong(eager).list) {
|
|
334
|
+
if (!cyclic(eager, members)) continue;
|
|
335
|
+
const component = found.of[members[0] as number] as number;
|
|
336
|
+
const list = inside.get(component);
|
|
337
|
+
if (list === undefined) inside.set(component, [members]);
|
|
338
|
+
else list.push(members);
|
|
339
|
+
}
|
|
340
|
+
const out: GraphCycle<Id>[] = [];
|
|
341
|
+
found.list.forEach((members, component) => {
|
|
342
|
+
if (!cyclic(every, members)) return;
|
|
343
|
+
const nodes = named(every, members);
|
|
344
|
+
const certain = inside.get(component) ?? [];
|
|
345
|
+
if (certain.length === 1 && certain[0]?.length === nodes.length) {
|
|
346
|
+
out.push({ nodes, certain: true });
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
out.push({ nodes, certain: false });
|
|
350
|
+
for (const part of certain) out.push({ nodes: named(eager, part), certain: true });
|
|
351
|
+
});
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* This function gives the nodes that a demand of a node needs first, deepest first (SCHED.LONG-CHAIN). It follows the
|
|
357
|
+
* eager edges with a target from the node, with its own stack. Each node comes after each node that it reads, except
|
|
358
|
+
* on a cycle, as in an order by heights. The walk costs only the nodes that it enters.
|
|
359
|
+
*
|
|
360
|
+
* @param from - The node of the demand. It is not in the answer.
|
|
361
|
+
* @param edgesOf - This function gives the edges of a node, or `undefined` for a node that the walk does not enter.
|
|
362
|
+
* @returns The nodes, deepest first.
|
|
363
|
+
*/
|
|
364
|
+
export function deepestFirst<Id>(from: Id, edgesOf: (id: Id) => readonly GraphEdge<Id>[] | undefined): readonly Id[] {
|
|
365
|
+
const out: Id[] = [];
|
|
366
|
+
const first = edgesOf(from);
|
|
367
|
+
if (first === undefined) return out;
|
|
368
|
+
const seen = new Set<Id>([from]);
|
|
369
|
+
const nodes: Id[] = [from];
|
|
370
|
+
const lists: (readonly GraphEdge<Id>[])[] = [first];
|
|
371
|
+
const positions: number[] = [0];
|
|
372
|
+
while (nodes.length > 0) {
|
|
373
|
+
const top = nodes.length - 1;
|
|
374
|
+
const edges = lists[top] as readonly GraphEdge<Id>[];
|
|
375
|
+
let position = positions[top] as number;
|
|
376
|
+
let entered = false;
|
|
377
|
+
while (position < edges.length) {
|
|
378
|
+
const edge = edges[position] as GraphEdge<Id>;
|
|
379
|
+
position += 1;
|
|
380
|
+
const to = edge.to;
|
|
381
|
+
if (edge.lazy || to === undefined || seen.has(to)) continue;
|
|
382
|
+
seen.add(to);
|
|
383
|
+
const more = edgesOf(to);
|
|
384
|
+
if (more === undefined) continue;
|
|
385
|
+
positions[top] = position;
|
|
386
|
+
nodes.push(to);
|
|
387
|
+
lists.push(more);
|
|
388
|
+
positions.push(0);
|
|
389
|
+
entered = true;
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
if (entered) continue;
|
|
393
|
+
const done = nodes.pop() as Id;
|
|
394
|
+
lists.pop();
|
|
395
|
+
positions.pop();
|
|
396
|
+
if (nodes.length > 0) out.push(done);
|
|
397
|
+
}
|
|
398
|
+
return out;
|
|
399
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,8 @@ export {
|
|
|
38
38
|
type Alias,
|
|
39
39
|
type CapsRule,
|
|
40
40
|
type Kind,
|
|
41
|
+
type KindFacts,
|
|
42
|
+
type LazyRule,
|
|
41
43
|
type Lookup,
|
|
42
44
|
type Namespace,
|
|
43
45
|
type NamesSource,
|
|
@@ -56,11 +58,13 @@ export {
|
|
|
56
58
|
lookupKind,
|
|
57
59
|
} from "./world.ts";
|
|
58
60
|
export {
|
|
61
|
+
type DomainExtension,
|
|
59
62
|
type MachinePart,
|
|
60
63
|
type MergeConflict,
|
|
61
64
|
type MergePath,
|
|
62
65
|
type WorldPart,
|
|
63
66
|
MergeError,
|
|
67
|
+
extendDomain,
|
|
64
68
|
mergeMachines,
|
|
65
69
|
mergeWorlds,
|
|
66
70
|
restrictMachine,
|
|
@@ -126,6 +130,7 @@ export {
|
|
|
126
130
|
type Perform,
|
|
127
131
|
type Plan,
|
|
128
132
|
type PrepareOptions,
|
|
133
|
+
type Read,
|
|
129
134
|
type Run,
|
|
130
135
|
type RunCode,
|
|
131
136
|
type RunFn,
|
|
@@ -178,30 +183,71 @@ export {
|
|
|
178
183
|
type DocVisit,
|
|
179
184
|
type Issue,
|
|
180
185
|
type RefUse,
|
|
186
|
+
type Typing,
|
|
181
187
|
type WalkOptions,
|
|
182
188
|
type WalkProblem,
|
|
183
189
|
check,
|
|
184
190
|
mapRefs,
|
|
185
191
|
refsOf,
|
|
192
|
+
typeCheck,
|
|
186
193
|
walk,
|
|
187
194
|
} from "./static.ts";
|
|
195
|
+
export { type DocFacts, type FactsOptions, type StaticRead, factsOf } from "./facts.ts";
|
|
196
|
+
export { type EffectsOptions, checkLatent, effectsOf } from "./effects.ts";
|
|
188
197
|
export {
|
|
189
198
|
type Closure,
|
|
190
199
|
type ExtFn,
|
|
191
200
|
type FormulaProfile,
|
|
192
201
|
type LazyOp,
|
|
193
202
|
type OpFn,
|
|
203
|
+
type Signature,
|
|
194
204
|
Held,
|
|
195
205
|
STD_EXT,
|
|
196
206
|
formulaEvaluators,
|
|
207
|
+
formulaFacts,
|
|
197
208
|
formulaKinds,
|
|
198
209
|
formulaKnown,
|
|
199
210
|
formulaMachine,
|
|
200
211
|
isClosure,
|
|
201
212
|
jsTruthy,
|
|
202
213
|
} from "./formula.ts";
|
|
214
|
+
export { decodeDomain, decodeValue, encodeValue } from "./codec.ts";
|
|
215
|
+
export {
|
|
216
|
+
type ParseResult,
|
|
217
|
+
type SyntaxAssoc,
|
|
218
|
+
type SyntaxCalls,
|
|
219
|
+
type SyntaxFixity,
|
|
220
|
+
type SyntaxForm,
|
|
221
|
+
type SyntaxIssue,
|
|
222
|
+
type SyntaxLiterals,
|
|
223
|
+
type SyntaxOperator,
|
|
224
|
+
type SyntaxProfile,
|
|
225
|
+
type SyntaxRefPattern,
|
|
226
|
+
type SyntaxRefs,
|
|
227
|
+
STD_SYNTAX,
|
|
228
|
+
parse,
|
|
229
|
+
print,
|
|
230
|
+
syntaxFormat,
|
|
231
|
+
} from "./syntax.ts";
|
|
203
232
|
export { type Definitions, type ReferenceOptions, STD_DEFINITIONS, formulaDefinitions, reference } from "./reference.ts";
|
|
204
233
|
export { type AgreeOptions, type Agreement, type Tier, type TierInput, type TierResult, agree, sameValue } from "./agree.ts";
|
|
234
|
+
export { type ConformOptions, type ConformProbe, type Finding, type Obligation, conforms } from "./conform.ts";
|
|
205
235
|
export { sameResult } from "./same.ts";
|
|
236
|
+
export { type ReadsCheck, type VerifyOptions, verifyReads } from "./reads.ts";
|
|
237
|
+
export { type Durable, type DurableCounts, type DurableOptions, type Epochs, durable } from "./durable.ts";
|
|
238
|
+
export {
|
|
239
|
+
type NodeEdge,
|
|
240
|
+
type NodeInfo,
|
|
241
|
+
type NodeRunOptions,
|
|
242
|
+
type Schedule,
|
|
243
|
+
type ScheduleCounts,
|
|
244
|
+
type ScheduleOptions,
|
|
245
|
+
edgesOf,
|
|
246
|
+
schedule,
|
|
247
|
+
} from "./schedule.ts";
|
|
248
|
+
export { type GraphCycle, type GraphEdge, type GraphNode, type GraphOptions, type NodeGraph, components, cycles, heights, necessary, nodeLayers } from "./graph.ts";
|
|
206
249
|
export { type Recorder, type ReplayEntry, type ReplayLog, type ReplayOptions, type ReplaySettle, recorder, replayer } from "./replay.ts";
|
|
207
250
|
export { type GenerateOptions, type Random, generate, random, typesOf } from "./generate.ts";
|
|
251
|
+
export { type Domain, type DomainCodec, type TypeIssue, type ValueType, STD_DOMAINS, fits, join, member, typeIssues, typeText } from "./types.ts";
|
|
252
|
+
export { type PartType, type RootTypes, type TypeInput, type TypeProblem, type TypeRule, type Typed, CAPS_TYPE } from "./typing.ts";
|
|
253
|
+
export { type SampleOptions, type TypeReading, typeFromSamples, typeFromSchema } from "./providers.ts";
|
package/src/ladder.ts
CHANGED
|
@@ -89,12 +89,51 @@ export function kindNamed(world: World, name: TypeRef): Kind | undefined {
|
|
|
89
89
|
return kindOf(world, name);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/** A test that passes each name. A rung of `answerOf` then answers with each entry that it has. */
|
|
93
|
+
const EACH_NAME = (): boolean => true;
|
|
94
|
+
|
|
95
|
+
/** The evaluator that a rung gives, without the record of the other rungs. */
|
|
96
|
+
export interface QuickAnswer {
|
|
97
|
+
readonly name: string;
|
|
98
|
+
readonly rung: Rung;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* This function finds the evaluator of a verb without the record of the rungs. It visits the rungs of `ask` in the
|
|
103
|
+
* same order: the own entry, then the kind, then the default of the world (VERB.LADDER). A plan without a trace,
|
|
104
|
+
* `deref` and the walkers of steps use it, thus each verb has one ladder.
|
|
105
|
+
*
|
|
106
|
+
* @param world - The world, for its defaults.
|
|
107
|
+
* @param own - The own entries of the document, when it has some.
|
|
108
|
+
* @param kind - The kind of the document. `undefined` skips the kind rung, as for a value without a type. `null` is
|
|
109
|
+
* a kind that the world does not have: the question stops there, as in `ask`.
|
|
110
|
+
* @param verb - The verb, for example `eval`, `format`, `deref` or `step`.
|
|
111
|
+
* @param has - The test of a name. A rung answers only with a name that passes it. The default passes each name.
|
|
112
|
+
* @returns The evaluator name and its rung, or `undefined` when no rung answers.
|
|
113
|
+
*/
|
|
114
|
+
export function answerOf(
|
|
115
|
+
world: World,
|
|
116
|
+
own: Readonly<Record<string, string>> | undefined,
|
|
117
|
+
kind: Kind | undefined | null,
|
|
118
|
+
verb: string,
|
|
119
|
+
has: (name: string) => boolean = EACH_NAME,
|
|
120
|
+
): QuickAnswer | undefined {
|
|
121
|
+
const ownName = entryOf(own, verb);
|
|
122
|
+
if (ownName !== undefined && has(ownName)) return { name: ownName, rung: "own" };
|
|
123
|
+
if (kind === null) return undefined;
|
|
124
|
+
const kindName = kind === undefined ? undefined : entryOf(kind.verbs, verb);
|
|
125
|
+
if (kindName !== undefined && has(kindName)) return { name: kindName, rung: "kind" };
|
|
126
|
+
const fallback = entryOf(world.defaults, verb);
|
|
127
|
+
return fallback !== undefined && has(fallback) ? { name: fallback, rung: "default" } : undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
92
130
|
/**
|
|
93
131
|
* This function asks a document a question, and gives the evaluator that answers it.
|
|
94
132
|
*
|
|
95
133
|
* The question visits the rungs in order: the own entry of the envelope, then its kind, then the default of the
|
|
96
134
|
* world. The type comes from the envelope, or else from the type that the position declares. The type is a
|
|
97
|
-
* reference into the world. The ladder does not guess a type from the shape of a value.
|
|
135
|
+
* reference into the world. The ladder does not guess a type from the shape of a value. Each verb has this ladder:
|
|
136
|
+
* `eval`, `deref`, `step` and each verb of a language (VERB.LADDER).
|
|
98
137
|
*
|
|
99
138
|
* @param world - The kinds and the defaults.
|
|
100
139
|
* @param doc - The header of the document: an envelope, or `{}` for a bare value.
|