@mark1russell7/coeval 0.4.3 → 0.6.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 -2
- package/build/index.d.ts +220 -128
- package/build/index.d.ts.map +1 -1
- package/build/index.js +624 -60
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/errors.ts +4 -2
- package/src/formula.ts +13 -19
- package/src/generate.ts +11 -3
- package/src/index.ts +2 -1
- package/src/merge.ts +387 -0
- package/src/reference.ts +147 -3
- package/src/stdlib.ts +180 -27
package/src/reference.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { type Envelope, type Json, type Key, isEnvelope, isRecord, typeName } fr
|
|
|
2
2
|
import { ask } from "./ladder.ts";
|
|
3
3
|
import type { CapsRule, Kind } from "./world.ts";
|
|
4
4
|
import { type PartSlot, type Split, partsOf, pathMatches } from "./parts.ts";
|
|
5
|
-
import { type Cap, type Context, type Resolution, type ResolveOptions, resolve, valueOfThing, worldThing } from "./deref.ts";
|
|
5
|
+
import { type Cap, type Context, type HostThing, type Resolution, type ResolveOptions, type Thing, THING, isHostThing, resolve, valueOfThing, worldThing } from "./deref.ts";
|
|
6
|
+
import { STD_EVALUATORS } from "./stdlib.ts";
|
|
6
7
|
import { type EvalError, type Failed, type Result, Failure, fail, ok, placed, thrown } from "./errors.ts";
|
|
7
8
|
import {
|
|
8
9
|
type AllOptions,
|
|
@@ -82,7 +83,10 @@ function own<T>(table: Readonly<Record<string, T>> | undefined, name: string): T
|
|
|
82
83
|
return table !== undefined && Object.hasOwn(table, name) ? table[name] : undefined;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* This function finds the code of an evaluator name. A runner wins over an evaluator of the same name. A standard
|
|
88
|
+
* evaluator gets the definition of the reference, unless the options give another definition (REF.TREE).
|
|
89
|
+
*/
|
|
86
90
|
function choose(env: Env, name: string): Chosen | undefined {
|
|
87
91
|
const runner = own(env.machine.runners, name);
|
|
88
92
|
if (runner !== undefined) {
|
|
@@ -92,7 +96,8 @@ function choose(env: Env, name: string): Chosen | undefined {
|
|
|
92
96
|
}
|
|
93
97
|
const evaluator = own(env.machine.evaluators, name);
|
|
94
98
|
if (evaluator === undefined) return undefined;
|
|
95
|
-
const
|
|
99
|
+
const standard = evaluator === own(STD_EVALUATORS, name) ? own(STD_DEFINITIONS.evaluators, name) : undefined;
|
|
100
|
+
const defined = own(env.definitions.evaluators, name) ?? standard;
|
|
96
101
|
if (defined !== undefined) return { evaluator: defined };
|
|
97
102
|
return typeof evaluator === "function" ? { evaluator } : { gap: `The reference has no definition of the compiled evaluator ${name}.` };
|
|
98
103
|
}
|
|
@@ -338,6 +343,145 @@ export function reference(machine: Machine, doc: unknown, context: Context = mak
|
|
|
338
343
|
}
|
|
339
344
|
}
|
|
340
345
|
|
|
346
|
+
/*
|
|
347
|
+
* The definition of the decorated tree (TREE.*), written from docs/SPEC.md. It shares no code with `tree.node` of
|
|
348
|
+
* stdlib.ts: its nodes, its memo, its marks for attributes in evaluation and its JSON are its own.
|
|
349
|
+
*/
|
|
350
|
+
|
|
351
|
+
/** One tree of the reference in one evaluation. */
|
|
352
|
+
interface RefTree {
|
|
353
|
+
open: boolean;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** A node of the reference. Its host thing is the key of the table `refNodes`. */
|
|
357
|
+
interface RefNode {
|
|
358
|
+
readonly tree: RefTree;
|
|
359
|
+
readonly kind: string;
|
|
360
|
+
readonly place: readonly Key[];
|
|
361
|
+
readonly parts: readonly Part[];
|
|
362
|
+
readonly ctx: Context;
|
|
363
|
+
/** The results of the attributes that ended. */
|
|
364
|
+
readonly values: Map<string, Result>;
|
|
365
|
+
/** The attributes in evaluation. */
|
|
366
|
+
readonly pending: Set<string>;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const refNodes = new WeakMap<object, RefNode>();
|
|
370
|
+
|
|
371
|
+
/** The attributes in evaluation in the reference, in the order of their demands (TREE.CYCLE). */
|
|
372
|
+
const inEvaluation: { readonly node: RefNode; readonly name: string }[] = [];
|
|
373
|
+
|
|
374
|
+
function nameOfAttribute(node: RefNode, name: string): string {
|
|
375
|
+
return `${name} of ${node.kind} at [${node.place.join(", ")}]`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function refNodeOf(thing: unknown): RefNode | undefined {
|
|
379
|
+
return typeof thing === "object" && thing !== null ? refNodes.get(thing) : undefined;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** TREE.ONCE, TREE.MEMO and TREE.CYCLE: the result of one attribute, or `undefined` when the node has no such part. */
|
|
383
|
+
function attributeOf(node: RefNode, self: HostThing, name: string): Result | undefined {
|
|
384
|
+
const done = node.values.get(name);
|
|
385
|
+
if (done !== undefined) return done;
|
|
386
|
+
if (node.pending.has(name)) {
|
|
387
|
+
const from = inEvaluation.findIndex((entry) => entry.node === node && entry.name === name);
|
|
388
|
+
const steps = from < 0 ? [nameOfAttribute(node, name)] : inEvaluation.slice(from).map((entry) => nameOfAttribute(entry.node, entry.name));
|
|
389
|
+
return fail("#CYCLE!", `The attributes form a cycle: ${[...steps, nameOfAttribute(node, name)].join(" -> ")}.`);
|
|
390
|
+
}
|
|
391
|
+
const part = node.parts.find((item) => item.key === name);
|
|
392
|
+
if (part === undefined) return undefined;
|
|
393
|
+
node.pending.add(name);
|
|
394
|
+
inEvaluation.push({ node, name });
|
|
395
|
+
try {
|
|
396
|
+
const result = part.evaluate({ ...node.ctx, self });
|
|
397
|
+
node.values.set(name, result);
|
|
398
|
+
return result;
|
|
399
|
+
} finally {
|
|
400
|
+
node.pending.delete(name);
|
|
401
|
+
inEvaluation.pop();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** TREE.RESULT: the JSON of a node. `open` holds the nodes and the containers whose JSON is in progress. */
|
|
406
|
+
function jsonOfNode(node: RefNode, self: HostThing, open: readonly object[]): Result {
|
|
407
|
+
const record: Record<string, unknown> = {};
|
|
408
|
+
for (const part of node.parts) {
|
|
409
|
+
const result = attributeOf(node, self, part.key) as Result;
|
|
410
|
+
if (!result.ok) return result;
|
|
411
|
+
const value = plainValue(result.value, [...open, self]);
|
|
412
|
+
if (!value.ok) return value;
|
|
413
|
+
setField(record, part.key, value.value);
|
|
414
|
+
}
|
|
415
|
+
return ok(record);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** This function gives a copy of a value with the JSON of each node in it. */
|
|
419
|
+
function plainValue(value: unknown, open: readonly object[]): Result {
|
|
420
|
+
const node = refNodeOf(value);
|
|
421
|
+
if (node !== undefined) {
|
|
422
|
+
if (open.includes(value as object)) return fail("#CYCLE!", `The JSON of the node ${node.kind} at [${node.place.join(", ")}] holds the node itself.`);
|
|
423
|
+
return jsonOfNode(node, value as HostThing, open);
|
|
424
|
+
}
|
|
425
|
+
if (typeof value !== "object" || value === null || isHostThing(value) || open.includes(value)) return ok(value);
|
|
426
|
+
if (Array.isArray(value)) {
|
|
427
|
+
const items: unknown[] = [];
|
|
428
|
+
for (const item of value) {
|
|
429
|
+
const plain = plainValue(item, [...open, value]);
|
|
430
|
+
if (!plain.ok) return plain;
|
|
431
|
+
items.push(plain.value);
|
|
432
|
+
}
|
|
433
|
+
return ok(items);
|
|
434
|
+
}
|
|
435
|
+
if (!isRecord(value)) return ok(value);
|
|
436
|
+
const copy: Record<string, unknown> = {};
|
|
437
|
+
for (const key of Object.keys(value)) {
|
|
438
|
+
const plain = plainValue(value[key], [...open, value]);
|
|
439
|
+
if (!plain.ok) return plain;
|
|
440
|
+
setField(copy, key, plain.value);
|
|
441
|
+
}
|
|
442
|
+
return ok(copy);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* TREE.LAZY, TREE.PARENT, TREE.SIBLING and TREE.ERROR: a node of an open tree gives its thing, and the root of a new
|
|
447
|
+
* tree gives its JSON. A step on the thing reads an attribute, and a failed attribute throws its own error.
|
|
448
|
+
*/
|
|
449
|
+
const treeDefinition: EvalFn = ({ parts, context, kind, place }) => {
|
|
450
|
+
const outer = refNodeOf(context.self);
|
|
451
|
+
const nested = outer !== undefined && outer.tree.open;
|
|
452
|
+
const node: RefNode = { tree: nested ? outer.tree : { open: true }, kind, place, parts, ctx: context, values: new Map(), pending: new Set() };
|
|
453
|
+
const self: HostThing & { readonly type: string } = {
|
|
454
|
+
[THING]: true,
|
|
455
|
+
type: kind,
|
|
456
|
+
label: `node ${kind}`,
|
|
457
|
+
deref: (key): Thing | undefined => {
|
|
458
|
+
if (key === "parent") return context.self;
|
|
459
|
+
if (key === "type") return kind;
|
|
460
|
+
const result = attributeOf(node, self, String(key));
|
|
461
|
+
if (result === undefined || result.ok) return result?.ok === true ? result.value : undefined;
|
|
462
|
+
throw new Failure(result.error);
|
|
463
|
+
},
|
|
464
|
+
toJson: () => {
|
|
465
|
+
const result = jsonOfNode(node, self, []);
|
|
466
|
+
if (result.ok) return result.value;
|
|
467
|
+
throw new Failure(result.error);
|
|
468
|
+
},
|
|
469
|
+
};
|
|
470
|
+
refNodes.set(self, node);
|
|
471
|
+
if (nested) return ok(self);
|
|
472
|
+
try {
|
|
473
|
+
return jsonOfNode(node, self, []);
|
|
474
|
+
} finally {
|
|
475
|
+
node.tree.open = false;
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* The definitions of the reference for the evaluators of the standard machine that need their own reading. The
|
|
481
|
+
* reference uses them for the standard evaluators, also without the option `definitions`. It holds `tree.node`.
|
|
482
|
+
*/
|
|
483
|
+
export const STD_DEFINITIONS: Definitions = { evaluators: { "tree.node": treeDefinition } };
|
|
484
|
+
|
|
341
485
|
/* The definitions of the formula kit: one function evaluator for each construct, written from docs/SPEC.md. */
|
|
342
486
|
|
|
343
487
|
const CLOSURE: unique symbol = Symbol.for("coeval.closure") as never;
|
package/src/stdlib.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Json, type Key, field, isRecord } from "./doc.ts";
|
|
2
|
-
import { type Context, type Evaluator, type Machine, type Part, type Result, type RunPart, type Runner, type Steps, fail, ok } from "./evaluate.ts";
|
|
2
|
+
import { type Context, type EvalInput, type Evaluator, type Machine, type Part, type Result, type RunPart, type Runner, type Steps, fail, ok } from "./evaluate.ts";
|
|
3
|
+
import { Failure } from "./errors.ts";
|
|
3
4
|
import { type HostThing, type Thing, type Walker, THING, isHostThing, valueOfThing } from "./deref.ts";
|
|
4
5
|
import type { World } from "./ladder.ts";
|
|
5
6
|
|
|
@@ -44,12 +45,15 @@ export const STD_WORLD: World = {
|
|
|
44
45
|
panel: {
|
|
45
46
|
verbs: { eval: "tree.node", view: "view.op" },
|
|
46
47
|
parts: { shape: "record", fields: ["width", "height", "children"], of: "number" },
|
|
47
|
-
|
|
48
|
+
// An attribute evaluates on demand, thus in an order that is not the order of the document. It causes no effect.
|
|
49
|
+
caps: [{ rule: "mask" }],
|
|
50
|
+
summary: "A node of a tree. Each part is an attribute that evaluates on demand, one time, with the node as self.",
|
|
48
51
|
},
|
|
49
52
|
box: {
|
|
50
53
|
verbs: { eval: "tree.node", view: "view.op" },
|
|
51
54
|
parts: { shape: "record", fields: ["width", "height", "children"], of: "number" },
|
|
52
|
-
|
|
55
|
+
caps: [{ rule: "mask" }],
|
|
56
|
+
summary: "A node of a tree, as panel. A formula in a box can read its parent and its siblings.",
|
|
53
57
|
},
|
|
54
58
|
math: {
|
|
55
59
|
kinds: {
|
|
@@ -149,51 +153,200 @@ export const STD_EVALUATORS: Readonly<Record<string, Evaluator>> = {
|
|
|
149
153
|
const found = resolve(data as readonly Json[]);
|
|
150
154
|
return found.ok ? ok(valueOfThing(found.thing)) : fail(found.code, found.reason);
|
|
151
155
|
},
|
|
152
|
-
"tree.node": (input) => treeNode(input
|
|
156
|
+
"tree.node": (input) => treeNode(input),
|
|
153
157
|
};
|
|
154
158
|
|
|
159
|
+
/*
|
|
160
|
+
* The decorated tree (TREE.*, R3 P1). Each node is a host thing, and each part of a node is an attribute. An attribute
|
|
161
|
+
* evaluates on demand, with the node as self. One memo cell for each node and attribute keeps its result for one
|
|
162
|
+
* evaluation. The structure comes first: a node inside an attribute of an open tree gives its thing and evaluates
|
|
163
|
+
* nothing. The root gives the JSON of the tree. Thus a reference to a sibling reads the finished list of children.
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
/** The brand of a node of the decorated tree. Its value is the state that the kit reads. */
|
|
167
|
+
const TREE_NODE: unique symbol = Symbol.for("coeval.tree.node") as never;
|
|
168
|
+
|
|
169
|
+
/** One tree in one evaluation. It is open until its root gives the JSON of the tree. */
|
|
170
|
+
interface TreeBuild {
|
|
171
|
+
open: boolean;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The state of a node behind its brand. */
|
|
175
|
+
interface NodeState {
|
|
176
|
+
readonly build: TreeBuild;
|
|
177
|
+
readonly type: string;
|
|
178
|
+
readonly place: readonly Key[];
|
|
179
|
+
/**
|
|
180
|
+
* This function gives the JSON of the node: each attribute in the order of the parts, with the JSON of each node in
|
|
181
|
+
* its value.
|
|
182
|
+
*
|
|
183
|
+
* @param path - The nodes and the containers whose JSON is in progress. A node on it gives `#CYCLE!`.
|
|
184
|
+
* @returns The JSON, or the first error.
|
|
185
|
+
*/
|
|
186
|
+
json(path: Set<object>): Result;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The memo cell of one attribute: in evaluation, or done with its result. */
|
|
190
|
+
type Cell = { readonly busy: true } | { readonly busy: false; readonly result: Result };
|
|
191
|
+
|
|
192
|
+
const BUSY: Cell = Object.freeze({ busy: true });
|
|
193
|
+
|
|
194
|
+
/** One attribute in evaluation. The text names it in the reason of a cycle. */
|
|
195
|
+
interface Demand {
|
|
196
|
+
readonly cells: Map<string, Cell>;
|
|
197
|
+
readonly key: string;
|
|
198
|
+
readonly text: string;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** The attributes in evaluation, in the order of their demands. An evaluation does not wait, thus one list serves. */
|
|
202
|
+
const demands: Demand[] = [];
|
|
203
|
+
|
|
204
|
+
function attributeText(key: string, type: string, place: readonly Key[]): string {
|
|
205
|
+
return `${key} of ${type} at [${place.join(", ")}]`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** This function gives the `#CYCLE!` of a demand that reached an attribute in evaluation, with the chain of demands. */
|
|
209
|
+
function cycleAt(cells: Map<string, Cell>, key: string, text: string): Result {
|
|
210
|
+
const first = demands.findIndex((demand) => demand.cells === cells && demand.key === key);
|
|
211
|
+
const chain = first === -1 ? [text] : demands.slice(first).map((demand) => demand.text);
|
|
212
|
+
chain.push(text);
|
|
213
|
+
return fail("#CYCLE!", `The attributes form a cycle: ${chain.join(" -> ")}.`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function stateOf(thing: unknown): NodeState | undefined {
|
|
217
|
+
return typeof thing === "object" && thing !== null ? (thing as { readonly [TREE_NODE]?: NodeState })[TREE_NODE] : undefined;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** This function sets an own field, also the field `__proto__` (SHAPE.PROTO). */
|
|
221
|
+
function putField(target: Record<string, unknown>, key: string, value: unknown): void {
|
|
222
|
+
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* This function gives a value with the JSON of each node of a tree in it. A list or a plain record is new only when
|
|
227
|
+
* it holds a node. A container that holds itself stays as it is.
|
|
228
|
+
*/
|
|
229
|
+
function plainOf(value: unknown, path: Set<object>): Result {
|
|
230
|
+
const state = stateOf(value);
|
|
231
|
+
if (state !== undefined) {
|
|
232
|
+
if (path.has(value as object)) return fail("#CYCLE!", `The JSON of the node ${state.type} at [${state.place.join(", ")}] holds the node itself.`);
|
|
233
|
+
return state.json(path);
|
|
234
|
+
}
|
|
235
|
+
if (typeof value !== "object" || value === null || path.has(value) || isHostThing(value)) return ok(value);
|
|
236
|
+
if (!Array.isArray(value) && !isRecord(value)) return ok(value);
|
|
237
|
+
path.add(value);
|
|
238
|
+
try {
|
|
239
|
+
if (Array.isArray(value)) {
|
|
240
|
+
let list: unknown[] | undefined;
|
|
241
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
242
|
+
const item = plainOf(value[index], path);
|
|
243
|
+
if (!item.ok) return item;
|
|
244
|
+
if (item.value !== value[index]) (list ??= [...(value as readonly unknown[])])[index] = item.value;
|
|
245
|
+
}
|
|
246
|
+
return ok(list ?? value);
|
|
247
|
+
}
|
|
248
|
+
const record = value as Readonly<Record<string, unknown>>;
|
|
249
|
+
let out: Record<string, unknown> | undefined;
|
|
250
|
+
for (const key of Object.keys(record)) {
|
|
251
|
+
const item = plainOf(record[key], path);
|
|
252
|
+
if (!item.ok) return item;
|
|
253
|
+
if (item.value === record[key]) continue;
|
|
254
|
+
if (out === undefined) {
|
|
255
|
+
out = {};
|
|
256
|
+
for (const other of Object.keys(record)) putField(out, other, record[other]);
|
|
257
|
+
}
|
|
258
|
+
putField(out, key, item.value);
|
|
259
|
+
}
|
|
260
|
+
return ok(out ?? value);
|
|
261
|
+
} finally {
|
|
262
|
+
path.delete(value);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
155
266
|
/**
|
|
156
|
-
* This function
|
|
157
|
-
*
|
|
267
|
+
* This function makes a node of the decorated tree. A step `parent` gives the self of the context of the node, and a
|
|
268
|
+
* step `type` gives its type. A step with the key of a part reads that attribute. A failed attribute throws `Failure`
|
|
269
|
+
* with its own error, thus a reference to it gives that error.
|
|
158
270
|
*/
|
|
159
|
-
function
|
|
160
|
-
const
|
|
271
|
+
function treeThing(parts: readonly Part[], context: Context, type: string, place: readonly Key[], build: TreeBuild): HostThing {
|
|
272
|
+
const cells = new Map<string, Cell>();
|
|
161
273
|
const read = (key: string): Result | undefined => {
|
|
162
|
-
const
|
|
163
|
-
if (
|
|
274
|
+
const cell = cells.get(key);
|
|
275
|
+
if (cell !== undefined) return cell.busy ? cycleAt(cells, key, attributeText(key, type, place)) : cell.result;
|
|
164
276
|
const part = parts.find((item) => item.key === key);
|
|
165
277
|
if (part === undefined) return undefined;
|
|
166
|
-
|
|
167
|
-
|
|
278
|
+
cells.set(key, BUSY);
|
|
279
|
+
demands.push({ cells, key, text: attributeText(key, type, place) });
|
|
280
|
+
let result: Result;
|
|
281
|
+
try {
|
|
282
|
+
result = part.evaluate({ ...context, self: node });
|
|
283
|
+
} catch (error) {
|
|
284
|
+
// An exception of the host, for example an overflow of the stack, leaves the attribute without a result.
|
|
285
|
+
cells.delete(key);
|
|
286
|
+
throw error;
|
|
287
|
+
} finally {
|
|
288
|
+
demands.pop();
|
|
289
|
+
}
|
|
290
|
+
cells.set(key, { busy: false, result });
|
|
168
291
|
return result;
|
|
169
292
|
};
|
|
170
|
-
const
|
|
293
|
+
const state: NodeState = {
|
|
294
|
+
build,
|
|
295
|
+
type,
|
|
296
|
+
place,
|
|
297
|
+
json(path) {
|
|
298
|
+
path.add(node);
|
|
299
|
+
try {
|
|
300
|
+
const out: Record<string, unknown> = {};
|
|
301
|
+
for (const part of parts) {
|
|
302
|
+
const result = read(part.key) as Result;
|
|
303
|
+
if (!result.ok) return result;
|
|
304
|
+
const value = plainOf(result.value, path);
|
|
305
|
+
if (!value.ok) return value;
|
|
306
|
+
putField(out, part.key, value.value);
|
|
307
|
+
}
|
|
308
|
+
return ok(out);
|
|
309
|
+
} finally {
|
|
310
|
+
path.delete(node);
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
const node: HostThing & { readonly type: string; readonly [TREE_NODE]: NodeState } = {
|
|
171
315
|
[THING]: true,
|
|
316
|
+
[TREE_NODE]: state,
|
|
172
317
|
type,
|
|
173
318
|
label: `node ${type}`,
|
|
174
319
|
deref(key: Key): Thing | undefined {
|
|
175
320
|
if (key === "parent") return context.self;
|
|
176
321
|
if (key === "type") return type;
|
|
177
322
|
const result = read(String(key));
|
|
178
|
-
|
|
323
|
+
if (result === undefined) return undefined;
|
|
324
|
+
if (!result.ok) throw new Failure(result.error);
|
|
325
|
+
return result.value;
|
|
179
326
|
},
|
|
180
327
|
toJson(): unknown {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
out[part.key] = result?.ok === true ? result.value : null;
|
|
185
|
-
}
|
|
186
|
-
return out;
|
|
328
|
+
const result = state.json(new Set());
|
|
329
|
+
if (!result.ok) throw new Failure(result.error);
|
|
330
|
+
return result.value;
|
|
187
331
|
},
|
|
188
332
|
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
333
|
+
return node;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* This function evaluates a node of the decorated tree. Inside an attribute of an open tree, the node gives its host
|
|
338
|
+
* thing, and no attribute evaluates. Else the node is the root of a new tree, and it gives the JSON of the tree.
|
|
339
|
+
*/
|
|
340
|
+
function treeNode({ parts, context, kind, place }: EvalInput): Result {
|
|
341
|
+
const above = stateOf(context.self);
|
|
342
|
+
if (above !== undefined && above.build.open) return ok(treeThing(parts, context, kind, place, above.build));
|
|
343
|
+
const build: TreeBuild = { open: true };
|
|
344
|
+
const root = treeThing(parts, context, kind, place, build);
|
|
345
|
+
try {
|
|
346
|
+
return (stateOf(root) as NodeState).json(new Set());
|
|
347
|
+
} finally {
|
|
348
|
+
build.open = false;
|
|
195
349
|
}
|
|
196
|
-
return ok(out);
|
|
197
350
|
}
|
|
198
351
|
|
|
199
352
|
/** The options of a walker `nearest`. */
|