@mark1russell7/coeval 0.19.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 +2 -1
- package/build/index.d.ts +151 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +689 -34
- package/build/index.js.map +1 -1
- package/package.json +3 -1
- package/src/index.ts +5 -0
- package/src/instances.ts +33 -5
- package/src/reference.ts +142 -12
- package/src/static.ts +8 -2
- package/src/stdlib.ts +367 -23
- package/src/steps.ts +181 -0
- package/src/world.ts +59 -0
package/build/index.js
CHANGED
|
@@ -354,6 +354,54 @@ function describe(value) {
|
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
356
|
// src/world.ts
|
|
357
|
+
/** The most rounds of a circular attribute without its own `limit` (TREE.CIRCULAR-LIMIT, D113). */
|
|
358
|
+
const CIRCULAR_LIMIT = 100;
|
|
359
|
+
/** This function tells if a value is JSON data: `null`, a truth value, a finite number, a text, or a container of them. */
|
|
360
|
+
function isJsonData(value) {
|
|
361
|
+
const stack = [value];
|
|
362
|
+
const seen = /* @__PURE__ */ new Set();
|
|
363
|
+
while (stack.length > 0) {
|
|
364
|
+
const item = stack.pop();
|
|
365
|
+
if (item === null || typeof item === "string" || typeof item === "boolean") continue;
|
|
366
|
+
if (typeof item === "number") {
|
|
367
|
+
if (!Number.isFinite(item)) return false;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (!Array.isArray(item) && !isRecord(item)) return false;
|
|
371
|
+
if (seen.has(item)) continue;
|
|
372
|
+
seen.add(item);
|
|
373
|
+
for (const inner of Object.values(item)) stack.push(inner);
|
|
374
|
+
}
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* This function reads the declaration `circular` of an attribute (D113). The tree kit, the reference, `check` and the
|
|
379
|
+
* graph of attribute instances read it with this one function.
|
|
380
|
+
*
|
|
381
|
+
* @param declared - The value of the field `circular`.
|
|
382
|
+
* @returns The start value and the limit, or the reason when the declaration has no correct form.
|
|
383
|
+
*/
|
|
384
|
+
function circularOf(declared) {
|
|
385
|
+
if (!isRecord(declared) || !Object.hasOwn(declared, "start")) return {
|
|
386
|
+
ok: false,
|
|
387
|
+
reason: "The declaration circular needs a record with the field start."
|
|
388
|
+
};
|
|
389
|
+
const start = field(declared, "start");
|
|
390
|
+
if (!isJsonData(start)) return {
|
|
391
|
+
ok: false,
|
|
392
|
+
reason: "The start value of the declaration circular must be JSON data."
|
|
393
|
+
};
|
|
394
|
+
const limit = Object.hasOwn(declared, "limit") ? field(declared, "limit") : 100;
|
|
395
|
+
if (typeof limit !== "number" || !Number.isSafeInteger(limit) || limit < 1) return {
|
|
396
|
+
ok: false,
|
|
397
|
+
reason: "The limit of the declaration circular must be a whole number of 1 or more."
|
|
398
|
+
};
|
|
399
|
+
return {
|
|
400
|
+
ok: true,
|
|
401
|
+
start,
|
|
402
|
+
limit
|
|
403
|
+
};
|
|
404
|
+
}
|
|
357
405
|
/**
|
|
358
406
|
* This function gives the type of a bare equation of a kind (D42). It is the type `of` of the shape of its parts, or
|
|
359
407
|
* else the type `of` of the shape `rest` of a record. The plan, the reference and `check` read it with this one function.
|
|
@@ -2388,6 +2436,143 @@ function ask(world, doc, verb, options = {}) {
|
|
|
2388
2436
|
steps
|
|
2389
2437
|
};
|
|
2390
2438
|
}
|
|
2439
|
+
// src/steps.ts
|
|
2440
|
+
/**
|
|
2441
|
+
* This function makes a ladder of steps, and it checks the declaration (STEPS.LADDER). The list of steps must have one
|
|
2442
|
+
* step or more, with no name two times. The step of each construct must be a step of the list.
|
|
2443
|
+
*
|
|
2444
|
+
* @param steps - The names of the steps, from the lowest to the highest.
|
|
2445
|
+
* @param constructs - The step that each construct needs.
|
|
2446
|
+
* @returns The ladder, frozen.
|
|
2447
|
+
* @throws TypeError for a declaration that is not valid, because it is a defect of the language.
|
|
2448
|
+
*/
|
|
2449
|
+
function stepLadder(steps, constructs) {
|
|
2450
|
+
if (steps.length === 0) throw new TypeError("a ladder of steps needs one step or more");
|
|
2451
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2452
|
+
for (const step of steps) {
|
|
2453
|
+
if (typeof step !== "string") throw new TypeError(`the step ${String(step)} is not a name`);
|
|
2454
|
+
if (seen.has(step)) throw new TypeError(`the step "${step}" is in the ladder two times`);
|
|
2455
|
+
seen.add(step);
|
|
2456
|
+
}
|
|
2457
|
+
for (const [construct, step] of Object.entries(constructs)) if (!seen.has(step)) throw new TypeError(`the construct "${construct}" needs the step "${String(step)}", which is not a step of the ladder`);
|
|
2458
|
+
return Object.freeze({
|
|
2459
|
+
steps: Object.freeze([...steps]),
|
|
2460
|
+
constructs: Object.freeze({ ...constructs })
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
/**
|
|
2464
|
+
* This function tells if a value is a step of a ladder. A host uses it to check its option for the highest step.
|
|
2465
|
+
*
|
|
2466
|
+
* @param ladder - The ladder.
|
|
2467
|
+
* @param value - The value.
|
|
2468
|
+
* @returns `true` when the value is the name of a step of the ladder.
|
|
2469
|
+
*/
|
|
2470
|
+
function isStep(ladder, value) {
|
|
2471
|
+
return typeof value === "string" && ladder.steps.includes(value);
|
|
2472
|
+
}
|
|
2473
|
+
/**
|
|
2474
|
+
* This function gives the position of a step in its ladder. The lowest step has the position 0.
|
|
2475
|
+
*
|
|
2476
|
+
* @param ladder - The ladder.
|
|
2477
|
+
* @param step - The step.
|
|
2478
|
+
* @returns The position.
|
|
2479
|
+
* @throws TypeError for a name that is not a step of the ladder.
|
|
2480
|
+
*/
|
|
2481
|
+
function stepRank(ladder, step) {
|
|
2482
|
+
const rank = ladder.steps.indexOf(step);
|
|
2483
|
+
if (rank < 0) throw new TypeError(`"${String(step)}" is not a step of the ladder. The steps are ${ladder.steps.join(", ")}`);
|
|
2484
|
+
return rank;
|
|
2485
|
+
}
|
|
2486
|
+
function pathOf(visit) {
|
|
2487
|
+
const path = [];
|
|
2488
|
+
for (let v = visit; v?.key !== void 0; v = v.parent) path.push(v.key);
|
|
2489
|
+
return path.reverse();
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* This function gives the lowest step that a document needs, with the reason (STEPS.LOWEST, STEPS.REASON). The step is
|
|
2493
|
+
* the highest step of the constructs of its nodes. The reason is the first node in document order with that step. The
|
|
2494
|
+
* walk uses its own stack, thus a deep document does not overflow (STEPS.LINEAR). It visits a shared node one time,
|
|
2495
|
+
* and it stops at the first node with the highest step of the ladder.
|
|
2496
|
+
*
|
|
2497
|
+
* @param ladder - The ladder of the language.
|
|
2498
|
+
* @param walk - The construct and the child nodes of a node.
|
|
2499
|
+
* @param root - The root node of the document.
|
|
2500
|
+
* @returns The step, and the reason when the step is not the lowest step.
|
|
2501
|
+
*/
|
|
2502
|
+
function stepOf(ladder, walk, root) {
|
|
2503
|
+
const top = ladder.steps.length - 1;
|
|
2504
|
+
let best = 0;
|
|
2505
|
+
let reason;
|
|
2506
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2507
|
+
const todo = [{
|
|
2508
|
+
node: root,
|
|
2509
|
+
key: void 0,
|
|
2510
|
+
parent: void 0
|
|
2511
|
+
}];
|
|
2512
|
+
for (let visit = todo.pop(); visit !== void 0; visit = todo.pop()) {
|
|
2513
|
+
const { node } = visit;
|
|
2514
|
+
if (typeof node === "object" && node !== null) {
|
|
2515
|
+
if (seen.has(node)) continue;
|
|
2516
|
+
seen.add(node);
|
|
2517
|
+
}
|
|
2518
|
+
const construct = walk.construct(node);
|
|
2519
|
+
const step = construct === void 0 || !Object.hasOwn(ladder.constructs, construct) ? void 0 : ladder.constructs[construct];
|
|
2520
|
+
const rank = step === void 0 ? 0 : ladder.steps.indexOf(step);
|
|
2521
|
+
if (rank > best) {
|
|
2522
|
+
best = rank;
|
|
2523
|
+
reason = {
|
|
2524
|
+
construct,
|
|
2525
|
+
step,
|
|
2526
|
+
path: pathOf(visit),
|
|
2527
|
+
node
|
|
2528
|
+
};
|
|
2529
|
+
if (best === top) break;
|
|
2530
|
+
}
|
|
2531
|
+
const kids = [...walk.children(node)];
|
|
2532
|
+
for (let i = kids.length - 1; i >= 0; i--) {
|
|
2533
|
+
const [key, child] = kids[i];
|
|
2534
|
+
todo.push({
|
|
2535
|
+
node: child,
|
|
2536
|
+
key,
|
|
2537
|
+
parent: visit
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
const step = ladder.steps[best];
|
|
2542
|
+
return reason === void 0 ? { step } : {
|
|
2543
|
+
step,
|
|
2544
|
+
reason
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
/**
|
|
2548
|
+
* This function compares the step of a document with the highest step that the host accepts (STEPS.MAX). The limit
|
|
2549
|
+
* changes only which documents the host accepts. The language evaluates an accepted document as before, thus a limit
|
|
2550
|
+
* does not change a value.
|
|
2551
|
+
*
|
|
2552
|
+
* @param ladder - The ladder of the language.
|
|
2553
|
+
* @param need - The step of the document, from `stepOf`.
|
|
2554
|
+
* @param maxStep - The highest step that the host accepts.
|
|
2555
|
+
* @returns `undefined` when the host accepts the document, or the refusal that names the needed step.
|
|
2556
|
+
* @throws TypeError when `maxStep` or the step of `need` is not a step of the ladder.
|
|
2557
|
+
*/
|
|
2558
|
+
function checkStep(ladder, need, maxStep) {
|
|
2559
|
+
const limit = stepRank(ladder, maxStep);
|
|
2560
|
+
if (stepRank(ladder, need.step) <= limit) return void 0;
|
|
2561
|
+
const head = `the document needs the step ${need.step}, and the host accepts the steps up to ${maxStep}`;
|
|
2562
|
+
const reason = need.reason;
|
|
2563
|
+
if (reason === void 0) return {
|
|
2564
|
+
needed: need.step,
|
|
2565
|
+
maxStep,
|
|
2566
|
+
message: head
|
|
2567
|
+
};
|
|
2568
|
+
const where = reason.path.length === 0 ? "at the root" : `at the path ${JSON.stringify(reason.path)}`;
|
|
2569
|
+
return {
|
|
2570
|
+
needed: need.step,
|
|
2571
|
+
maxStep,
|
|
2572
|
+
reason,
|
|
2573
|
+
message: `${head}. The construct "${reason.construct}" ${where} needs the step ${reason.step}`
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2391
2576
|
// src/deref.ts
|
|
2392
2577
|
/**
|
|
2393
2578
|
* The brand of a host thing: an object of the host that answers the steps of a path itself. The brand is a
|
|
@@ -5622,12 +5807,55 @@ const NO_ANSWER = /* @__PURE__ */ Object.freeze({
|
|
|
5622
5807
|
by: void 0
|
|
5623
5808
|
});
|
|
5624
5809
|
/**
|
|
5625
|
-
* The marks of the attributes in evaluation, for each
|
|
5626
|
-
* two evaluations share their
|
|
5810
|
+
* The marks of the attributes in evaluation, for each key of a node, with the cells of a host (TREE.CELL). Nodes of
|
|
5811
|
+
* two evaluations share their key and their kept cells, thus they share their marks. Nodes of two documents have
|
|
5812
|
+
* different keys, thus a nested evaluation gets its own marks (TREE.CELL-NESTED, D115).
|
|
5627
5813
|
*/
|
|
5628
5814
|
const marksByPlace = /* @__PURE__ */ new WeakMap();
|
|
5629
|
-
/** The trees of the cells of a host, for each
|
|
5815
|
+
/** The trees of the cells of a host, for each key of a root. Nodes of two evaluations stay in one tree. */
|
|
5630
5816
|
const buildsByPlace = /* @__PURE__ */ new WeakMap();
|
|
5817
|
+
/** The keys of the nodes with the cells of a host, for each scope and each place of a plan (D115). */
|
|
5818
|
+
const keysByScope = /* @__PURE__ */ new WeakMap();
|
|
5819
|
+
/** The scopes of the roots that a demand made, for each marks of a node and each name of an attribute (D115). */
|
|
5820
|
+
const demandScopes = /* @__PURE__ */ new WeakMap();
|
|
5821
|
+
/**
|
|
5822
|
+
* This function gives the key of a node with the cells of a host (TREE.CELL-DOCUMENT, D115). The key of the node
|
|
5823
|
+
* around the node is its scope. A root without such a node has the scope of the open demand, or no scope.
|
|
5824
|
+
* Without a scope, the key is the place of the plan. Thus a cached plan gives a new key for the nodes of each
|
|
5825
|
+
* document, and each evaluation of a document gives the same keys.
|
|
5826
|
+
*
|
|
5827
|
+
* @param place - The place of the document of the node in its plan.
|
|
5828
|
+
* @param above - The state of the node around the node, or `undefined`.
|
|
5829
|
+
* @returns The key: an array with the keys of `place`.
|
|
5830
|
+
*/
|
|
5831
|
+
function cellPlace(place, above) {
|
|
5832
|
+
let scope = above?.place;
|
|
5833
|
+
if (scope === void 0) {
|
|
5834
|
+
const demand = demands[demands.length - 1];
|
|
5835
|
+
if (demand === void 0) return place;
|
|
5836
|
+
let byName = demandScopes.get(demand.cells);
|
|
5837
|
+
if (byName === void 0) {
|
|
5838
|
+
byName = /* @__PURE__ */ new Map();
|
|
5839
|
+
demandScopes.set(demand.cells, byName);
|
|
5840
|
+
}
|
|
5841
|
+
scope = byName.get(demand.key);
|
|
5842
|
+
if (scope === void 0) {
|
|
5843
|
+
scope = {};
|
|
5844
|
+
byName.set(demand.key, scope);
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
let keys = keysByScope.get(scope);
|
|
5848
|
+
if (keys === void 0) {
|
|
5849
|
+
keys = /* @__PURE__ */ new WeakMap();
|
|
5850
|
+
keysByScope.set(scope, keys);
|
|
5851
|
+
}
|
|
5852
|
+
let key = keys.get(place);
|
|
5853
|
+
if (key === void 0) {
|
|
5854
|
+
key = Object.freeze([...place]);
|
|
5855
|
+
keys.set(place, key);
|
|
5856
|
+
}
|
|
5857
|
+
return key;
|
|
5858
|
+
}
|
|
5631
5859
|
/** This function gives the value of a weak map, or puts a new value in it. */
|
|
5632
5860
|
function keptAt(map, place, make) {
|
|
5633
5861
|
let value = map.get(place);
|
|
@@ -5641,6 +5869,277 @@ function keptAt(map, place, make) {
|
|
|
5641
5869
|
const NOT_THROWN = /* @__PURE__ */ Symbol("coeval.tree.not-thrown");
|
|
5642
5870
|
/** The attributes in evaluation, in the order of their demands. An evaluation does not wait, thus one list serves. */
|
|
5643
5871
|
const demands = [];
|
|
5872
|
+
/** The open fixed point, or `undefined`. */
|
|
5873
|
+
let openPoint;
|
|
5874
|
+
/** For each table `tree.attributes`, `true` when an attribute of it declares `circular`. */
|
|
5875
|
+
const circularTables = /* @__PURE__ */ new WeakMap();
|
|
5876
|
+
/** For each declaration `circular` that is an object, its rule. */
|
|
5877
|
+
const circularRules = /* @__PURE__ */ new WeakMap();
|
|
5878
|
+
/** This function tells if a tree contract declares a circular attribute. The answer for a table is computed one time. */
|
|
5879
|
+
function hasCircular(tree) {
|
|
5880
|
+
const table = tree?.attributes;
|
|
5881
|
+
if (table === void 0) return false;
|
|
5882
|
+
let known = circularTables.get(table);
|
|
5883
|
+
if (known === void 0) {
|
|
5884
|
+
known = Object.keys(table).some((key) => {
|
|
5885
|
+
const declared = entryOf(table, key);
|
|
5886
|
+
return typeof declared === "object" && declared !== null && declared.circular !== void 0;
|
|
5887
|
+
});
|
|
5888
|
+
circularTables.set(table, known);
|
|
5889
|
+
}
|
|
5890
|
+
return known;
|
|
5891
|
+
}
|
|
5892
|
+
/** This function gives the declaration `circular` of an attribute of a tree contract (D113), or `undefined`. */
|
|
5893
|
+
function circularRule(tree, key) {
|
|
5894
|
+
const declared = entryOf(tree?.attributes, key)?.circular;
|
|
5895
|
+
if (declared === void 0) return void 0;
|
|
5896
|
+
if (typeof declared !== "object" || declared === null) return circularOf(declared);
|
|
5897
|
+
let rule = circularRules.get(declared);
|
|
5898
|
+
if (rule === void 0) {
|
|
5899
|
+
rule = circularOf(declared);
|
|
5900
|
+
circularRules.set(declared, rule);
|
|
5901
|
+
}
|
|
5902
|
+
return rule;
|
|
5903
|
+
}
|
|
5904
|
+
/** This function gives the values of a fixed point for the marks of a node. */
|
|
5905
|
+
function loopValues(loop, marks) {
|
|
5906
|
+
let values = loop.values.get(marks);
|
|
5907
|
+
if (values === void 0) {
|
|
5908
|
+
values = /* @__PURE__ */ new Map();
|
|
5909
|
+
loop.values.set(marks, values);
|
|
5910
|
+
}
|
|
5911
|
+
return values;
|
|
5912
|
+
}
|
|
5913
|
+
/** This function marks each demand above the leader: each one read an approximation, thus its result is not final. */
|
|
5914
|
+
function markTentative(loop) {
|
|
5915
|
+
for (let index = loop.depth + 1; index < demands.length; index += 1) loop.tentative.add(demands[index]);
|
|
5916
|
+
}
|
|
5917
|
+
/** This function tells if two approximations are the same: two values by DOC.EQUALS, two errors by code and reason. */
|
|
5918
|
+
function sameApproximation$1(a, b) {
|
|
5919
|
+
if (a === void 0 || b === void 0) return a === b;
|
|
5920
|
+
if (a.ok && b.ok) return docEquals(a.value, b.value);
|
|
5921
|
+
if (!a.ok && !b.ok) return a.error.code === b.error.code && a.error.reason === b.error.reason;
|
|
5922
|
+
return false;
|
|
5923
|
+
}
|
|
5924
|
+
/** This function writes the final value of each circular attribute of the last round in the plain memo of its node. */
|
|
5925
|
+
function settle(loop) {
|
|
5926
|
+
for (const values of loop.values.values()) for (const [key, value] of values) if (value.memo !== void 0 && value.round === loop.round) value.memo.set(key, {
|
|
5927
|
+
busy: false,
|
|
5928
|
+
result: value.result,
|
|
5929
|
+
by: value.by
|
|
5930
|
+
});
|
|
5931
|
+
}
|
|
5932
|
+
/** The text of the fixed point of a circular attribute that does not become stable (TREE.CIRCULAR-LIMIT). */
|
|
5933
|
+
function limitReason(text, limit) {
|
|
5934
|
+
return `The cycle of the circular attribute ${text} does not become stable within its limit of ${limit} ${limit === 1 ? "round" : "rounds"}.`;
|
|
5935
|
+
}
|
|
5936
|
+
/**
|
|
5937
|
+
* This function answers a demand that reached an attribute in evaluation in an open fixed point (TREE.CYCLE). A chain
|
|
5938
|
+
* of circular attributes gives the current approximation (TREE.CIRCULAR). Each other chain gives `#CYCLE!`.
|
|
5939
|
+
*/
|
|
5940
|
+
function reachedIn(loop, n, key) {
|
|
5941
|
+
const first = demands.findIndex((demand) => demand.cells === n.marks && demand.key === key);
|
|
5942
|
+
let circular = first >= loop.depth;
|
|
5943
|
+
for (let index = first; circular && index < demands.length; index += 1) circular = loop.circular.has(demands[index]);
|
|
5944
|
+
if (!circular) return cycleAt(n.marks, key, attributeText(key, n.type, n.place));
|
|
5945
|
+
const rule = circularRule(n.tree, key);
|
|
5946
|
+
const kept = loop.values.get(n.marks)?.get(key);
|
|
5947
|
+
markTentative(loop);
|
|
5948
|
+
return kept !== void 0 ? kept.result : ok(rule?.ok === true ? rule.start : null);
|
|
5949
|
+
}
|
|
5950
|
+
/**
|
|
5951
|
+
* This function evaluates the leader of a fixed point (TREE.CIRCULAR, D113). Its demand is the last one. Each round
|
|
5952
|
+
* visits the ladder again, and the rounds end when no circular attribute gets a new value. An error ends the rounds
|
|
5953
|
+
* at once. After the last round of the limit, the leader gives `#CYCLE!` at the place of its node.
|
|
5954
|
+
*/
|
|
5955
|
+
function roundsOf(n, key, rule) {
|
|
5956
|
+
const leader = demands[demands.length - 1];
|
|
5957
|
+
const loop = {
|
|
5958
|
+
depth: demands.length - 1,
|
|
5959
|
+
round: 0,
|
|
5960
|
+
changed: false,
|
|
5961
|
+
values: /* @__PURE__ */ new Map(),
|
|
5962
|
+
circular: /* @__PURE__ */ new Set([leader]),
|
|
5963
|
+
tentative: /* @__PURE__ */ new Set()
|
|
5964
|
+
};
|
|
5965
|
+
openPoint = loop;
|
|
5966
|
+
try {
|
|
5967
|
+
let before = ok(rule.start);
|
|
5968
|
+
for (;;) {
|
|
5969
|
+
loop.round += 1;
|
|
5970
|
+
loop.changed = false;
|
|
5971
|
+
const answer = n.climb(key) ?? NO_ANSWER;
|
|
5972
|
+
if (answer.result !== void 0 && !answer.result.ok) return answer;
|
|
5973
|
+
if (!sameApproximation$1(before, answer.result)) loop.changed = true;
|
|
5974
|
+
before = answer.result;
|
|
5975
|
+
loopValues(loop, n.marks).set(key, {
|
|
5976
|
+
result: answer.result,
|
|
5977
|
+
by: answer.by,
|
|
5978
|
+
round: loop.round,
|
|
5979
|
+
memo: void 0
|
|
5980
|
+
});
|
|
5981
|
+
if (!loop.changed) {
|
|
5982
|
+
settle(loop);
|
|
5983
|
+
return answer;
|
|
5984
|
+
}
|
|
5985
|
+
if (loop.round >= rule.limit) return {
|
|
5986
|
+
result: fail("#CYCLE!", limitReason(attributeText(key, n.type, n.place), rule.limit), { place: n.place }),
|
|
5987
|
+
by: answer.by
|
|
5988
|
+
};
|
|
5989
|
+
}
|
|
5990
|
+
} finally {
|
|
5991
|
+
openPoint = void 0;
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
/**
|
|
5995
|
+
* This function evaluates a circular attribute in the open fixed point of another leader, one time in each round
|
|
5996
|
+
* (D113). A later demand in the same round gets the value of the round. Each value is an approximation.
|
|
5997
|
+
*/
|
|
5998
|
+
function roundIn(loop, n, key, rule, kept) {
|
|
5999
|
+
let value = kept;
|
|
6000
|
+
if (value === void 0 || value.round !== loop.round) {
|
|
6001
|
+
const demand = {
|
|
6002
|
+
cells: n.marks,
|
|
6003
|
+
key,
|
|
6004
|
+
text: attributeText(key, n.type, n.place)
|
|
6005
|
+
};
|
|
6006
|
+
n.marks.set(key, BUSY);
|
|
6007
|
+
demands.push(demand);
|
|
6008
|
+
loop.circular.add(demand);
|
|
6009
|
+
let answer;
|
|
6010
|
+
try {
|
|
6011
|
+
answer = n.climb(key) ?? NO_ANSWER;
|
|
6012
|
+
} finally {
|
|
6013
|
+
demands.pop();
|
|
6014
|
+
n.marks.delete(key);
|
|
6015
|
+
}
|
|
6016
|
+
if (!sameApproximation$1(value === void 0 ? ok(rule.start) : value.result, answer.result)) loop.changed = true;
|
|
6017
|
+
value = {
|
|
6018
|
+
result: answer.result,
|
|
6019
|
+
by: answer.by,
|
|
6020
|
+
round: loop.round,
|
|
6021
|
+
memo: n.host === void 0 ? n.cells : void 0
|
|
6022
|
+
};
|
|
6023
|
+
loopValues(loop, n.marks).set(key, value);
|
|
6024
|
+
}
|
|
6025
|
+
markTentative(loop);
|
|
6026
|
+
return value.result;
|
|
6027
|
+
}
|
|
6028
|
+
/**
|
|
6029
|
+
* This function evaluates an attribute without the cell of the host: with the plain memo, or in an open fixed point
|
|
6030
|
+
* (D113). A result that read an approximation is not final, thus no memo keeps it.
|
|
6031
|
+
*/
|
|
6032
|
+
function localIn(n, key, rule) {
|
|
6033
|
+
const demand = {
|
|
6034
|
+
cells: n.marks,
|
|
6035
|
+
key,
|
|
6036
|
+
text: attributeText(key, n.type, n.place)
|
|
6037
|
+
};
|
|
6038
|
+
const loop = openPoint;
|
|
6039
|
+
n.marks.set(key, BUSY);
|
|
6040
|
+
demands.push(demand);
|
|
6041
|
+
let answer;
|
|
6042
|
+
try {
|
|
6043
|
+
answer = rule === void 0 ? n.climb(key) ?? NO_ANSWER : roundsOf(n, key, rule);
|
|
6044
|
+
} catch (error) {
|
|
6045
|
+
n.marks.delete(key);
|
|
6046
|
+
throw error;
|
|
6047
|
+
} finally {
|
|
6048
|
+
demands.pop();
|
|
6049
|
+
}
|
|
6050
|
+
if (loop !== void 0 && loop.tentative.has(demand)) {
|
|
6051
|
+
n.marks.delete(key);
|
|
6052
|
+
return answer.result;
|
|
6053
|
+
}
|
|
6054
|
+
if (n.host !== void 0) {
|
|
6055
|
+
n.marks.delete(key);
|
|
6056
|
+
if (loop !== void 0) loopValues(loop, n.marks).set(key, {
|
|
6057
|
+
result: answer.result,
|
|
6058
|
+
by: answer.by,
|
|
6059
|
+
round: 0,
|
|
6060
|
+
memo: void 0
|
|
6061
|
+
});
|
|
6062
|
+
}
|
|
6063
|
+
n.cells.set(key, {
|
|
6064
|
+
busy: false,
|
|
6065
|
+
result: answer.result,
|
|
6066
|
+
by: answer.by
|
|
6067
|
+
});
|
|
6068
|
+
return answer.result;
|
|
6069
|
+
}
|
|
6070
|
+
/**
|
|
6071
|
+
* This function answers a demand that reached an attribute in evaluation (TREE.CYCLE). Outside a fixed point, it gives
|
|
6072
|
+
* `#CYCLE!` with the chain. In an open fixed point, `reachedIn` answers (TREE.CIRCULAR).
|
|
6073
|
+
*/
|
|
6074
|
+
function busyAt(n, key) {
|
|
6075
|
+
const loop = openPoint;
|
|
6076
|
+
return loop === void 0 ? cycleAt(n.marks, key, attributeText(key, n.type, n.place)) : reachedIn(loop, n, key);
|
|
6077
|
+
}
|
|
6078
|
+
/**
|
|
6079
|
+
* This function evaluates the leader of a fixed point through the cell of the host (TREE.CELL, D113). The cell of the
|
|
6080
|
+
* leader gets the final value of the fixed point, from a computation that contains each round.
|
|
6081
|
+
*/
|
|
6082
|
+
function leaderOnHost(n, key, rule, host) {
|
|
6083
|
+
const text = attributeText(key, n.type, n.place);
|
|
6084
|
+
n.marks.set(key, BUSY);
|
|
6085
|
+
demands.push({
|
|
6086
|
+
cells: n.marks,
|
|
6087
|
+
key,
|
|
6088
|
+
text
|
|
6089
|
+
});
|
|
6090
|
+
let thrown = NOT_THROWN;
|
|
6091
|
+
let answer;
|
|
6092
|
+
try {
|
|
6093
|
+
answer = host(n.place, key, () => {
|
|
6094
|
+
try {
|
|
6095
|
+
return roundsOf(n, key, rule);
|
|
6096
|
+
} catch (error) {
|
|
6097
|
+
thrown = error;
|
|
6098
|
+
throw error;
|
|
6099
|
+
}
|
|
6100
|
+
});
|
|
6101
|
+
} catch (error) {
|
|
6102
|
+
if (error === thrown) throw error;
|
|
6103
|
+
answer = {
|
|
6104
|
+
result: fail("#CYCLE!", `The cells of the host found a cycle in the demands: ${demands.map((demand) => demand.text).join(" -> ")}.`, { cause: error }),
|
|
6105
|
+
by: void 0
|
|
6106
|
+
};
|
|
6107
|
+
} finally {
|
|
6108
|
+
demands.pop();
|
|
6109
|
+
n.marks.delete(key);
|
|
6110
|
+
}
|
|
6111
|
+
n.cells.set(key, {
|
|
6112
|
+
busy: false,
|
|
6113
|
+
result: answer.result,
|
|
6114
|
+
by: answer.by
|
|
6115
|
+
});
|
|
6116
|
+
return answer.result;
|
|
6117
|
+
}
|
|
6118
|
+
/**
|
|
6119
|
+
* This function gives an attribute of a node with circular attributes, or an attribute in an open fixed point (D113).
|
|
6120
|
+
* The memo and the marks of the node do not hold the attribute. An open fixed point does not use the cells of the host.
|
|
6121
|
+
*/
|
|
6122
|
+
function circularRead(n, key) {
|
|
6123
|
+
const rule = circularRule(n.tree, key);
|
|
6124
|
+
if (rule !== void 0 && !rule.ok) {
|
|
6125
|
+
const result = fail("#VALUE!", `The attribute ${attributeText(key, n.type, n.place)} has a bad declaration circular. ${rule.reason}`);
|
|
6126
|
+
n.cells.set(key, {
|
|
6127
|
+
busy: false,
|
|
6128
|
+
result,
|
|
6129
|
+
by: void 0
|
|
6130
|
+
});
|
|
6131
|
+
return result;
|
|
6132
|
+
}
|
|
6133
|
+
const loop = openPoint;
|
|
6134
|
+
if (loop !== void 0) {
|
|
6135
|
+
const kept = loop.values.get(n.marks)?.get(key);
|
|
6136
|
+
if (rule !== void 0) return roundIn(loop, n, key, rule, kept);
|
|
6137
|
+
if (kept !== void 0) return kept.result;
|
|
6138
|
+
return localIn(n, key, void 0);
|
|
6139
|
+
}
|
|
6140
|
+
if (rule === void 0 || n.host === void 0) return localIn(n, key, rule);
|
|
6141
|
+
return leaderOnHost(n, key, rule, n.host);
|
|
6142
|
+
}
|
|
5644
6143
|
function attributeText(key, type, place) {
|
|
5645
6144
|
return `${key} of ${type} at [${place.join(", ")}]`;
|
|
5646
6145
|
}
|
|
@@ -5710,16 +6209,18 @@ function plainOf(value, path) {
|
|
|
5710
6209
|
* fold or a default, come last. A failed attribute throws `Failure` with its own error, thus a reference to it gives that error.
|
|
5711
6210
|
* The argument `outer` is the self of the context, that `treeNode` resolved. The node reads no root of the context directly (C4).
|
|
5712
6211
|
*
|
|
5713
|
-
* The argument `
|
|
6212
|
+
* The argument `place` is the key of the node. With the cells of a host, `cellPlace` gives it. Else it is the place of
|
|
6213
|
+
* the plan. The argument `host` gives the memo cells of the host (TREE.CELL), or `undefined` for the plain memo of the node.
|
|
5714
6214
|
*/
|
|
5715
|
-
function treeThing(input, build, outer, host) {
|
|
5716
|
-
const { parts, context, kind: type,
|
|
6215
|
+
function treeThing(input, place, build, outer, host) {
|
|
6216
|
+
const { parts, context, kind: type, machine } = input;
|
|
5717
6217
|
const runtime = input[RUNTIME];
|
|
5718
6218
|
const { tree, of } = contractOf$1(machine, input.typePath);
|
|
5719
6219
|
const above = stateOf(outer);
|
|
5720
6220
|
const up = above !== void 0 && above.build === build ? above : void 0;
|
|
5721
6221
|
const cells = /* @__PURE__ */ new Map();
|
|
5722
6222
|
const marks = host === void 0 ? cells : keptAt(marksByPlace, place, () => /* @__PURE__ */ new Map());
|
|
6223
|
+
const circular = hasCircular(tree);
|
|
5723
6224
|
/** The positions of the items of the list `children`, for the names of the children. */
|
|
5724
6225
|
let positions;
|
|
5725
6226
|
const fold = (key, declared) => {
|
|
@@ -5789,8 +6290,9 @@ function treeThing(input, build, outer, host) {
|
|
|
5789
6290
|
*/
|
|
5790
6291
|
const through = host === void 0 ? void 0 : (key) => {
|
|
5791
6292
|
const text = attributeText(key, type, place);
|
|
5792
|
-
if (marks.has(key)) return
|
|
6293
|
+
if (marks.has(key)) return busyAt(state, key);
|
|
5793
6294
|
if (tree === void 0 && up?.tree === void 0 && !parts.some((item) => item.key === key)) return void 0;
|
|
6295
|
+
if (circular || openPoint !== void 0) return circularRead(state, key);
|
|
5794
6296
|
marks.set(key, BUSY);
|
|
5795
6297
|
demands.push({
|
|
5796
6298
|
cells: marks,
|
|
@@ -5828,8 +6330,9 @@ function treeThing(input, build, outer, host) {
|
|
|
5828
6330
|
const read = (key) => {
|
|
5829
6331
|
if (through !== void 0) return through(key);
|
|
5830
6332
|
const cell = cells.get(key);
|
|
5831
|
-
if (cell !== void 0) return cell.busy ?
|
|
6333
|
+
if (cell !== void 0) return cell.busy ? busyAt(state, key) : cell.result;
|
|
5832
6334
|
if (tree === void 0 && up?.tree === void 0 && !parts.some((item) => item.key === key)) return void 0;
|
|
6335
|
+
if (circular || openPoint !== void 0) return circularRead(state, key);
|
|
5833
6336
|
cells.set(key, BUSY);
|
|
5834
6337
|
demands.push({
|
|
5835
6338
|
cells,
|
|
@@ -5857,6 +6360,10 @@ function treeThing(input, build, outer, host) {
|
|
|
5857
6360
|
type,
|
|
5858
6361
|
place,
|
|
5859
6362
|
tree,
|
|
6363
|
+
marks,
|
|
6364
|
+
cells,
|
|
6365
|
+
host,
|
|
6366
|
+
climb,
|
|
5860
6367
|
read,
|
|
5861
6368
|
equation(term, names) {
|
|
5862
6369
|
if (machine === void 0) return fail("#NOEVAL", "The evaluation of an equation needs the machine.");
|
|
@@ -5980,10 +6487,11 @@ function treeNode(input, host) {
|
|
|
5980
6487
|
const found = input.resolve(SELF_PATH, NO_HOPS);
|
|
5981
6488
|
const outer = found.ok ? found.thing : void 0;
|
|
5982
6489
|
const above = stateOf(outer);
|
|
5983
|
-
|
|
5984
|
-
|
|
6490
|
+
const place = host === void 0 ? input.place : cellPlace(input.place, above);
|
|
6491
|
+
if (above !== void 0 && above.build.open) return ok(treeThing(input, place, above.build, outer, host));
|
|
6492
|
+
const build = host === void 0 ? { open: true } : keptAt(buildsByPlace, place, () => ({ open: true }));
|
|
5985
6493
|
build.open = true;
|
|
5986
|
-
const root = treeThing(input, build, outer, host);
|
|
6494
|
+
const root = treeThing(input, place, build, outer, host);
|
|
5987
6495
|
try {
|
|
5988
6496
|
return stateOf(root).json(/* @__PURE__ */ new Set());
|
|
5989
6497
|
} finally {
|
|
@@ -7465,16 +7973,24 @@ function valueOf$1(info) {
|
|
|
7465
7973
|
function hasOwnEntry(table, name) {
|
|
7466
7974
|
return table !== void 0 && Object.hasOwn(table, name);
|
|
7467
7975
|
}
|
|
7976
|
+
/** This function tells if the kind of a node declares an attribute `circular` (D113). */
|
|
7977
|
+
function isCircular(kind, attribute) {
|
|
7978
|
+
return (hasOwnEntry(kind.tree?.attributes, attribute) ? kind.tree?.attributes?.[attribute] : void 0)?.circular !== void 0;
|
|
7979
|
+
}
|
|
7468
7980
|
/** This function gives the vertex of an attribute of a node. A new vertex goes on the work list for its rungs. */
|
|
7469
7981
|
function vertexOf(b, node, attribute) {
|
|
7470
7982
|
const id = attributeVertexId(node.visit.place, attribute);
|
|
7471
7983
|
if (!b.vertices.has(id)) {
|
|
7472
|
-
|
|
7984
|
+
const vertex = {
|
|
7473
7985
|
id,
|
|
7474
7986
|
place: node.visit.place,
|
|
7475
7987
|
attribute,
|
|
7476
7988
|
type: node.text
|
|
7477
|
-
}
|
|
7989
|
+
};
|
|
7990
|
+
b.vertices.set(id, isCircular(node.kind, attribute) ? {
|
|
7991
|
+
...vertex,
|
|
7992
|
+
circular: true
|
|
7993
|
+
} : vertex);
|
|
7478
7994
|
b.nodes.set(id, node);
|
|
7479
7995
|
if (!node.parts.has(attribute)) b.work.push(id);
|
|
7480
7996
|
}
|
|
@@ -7873,12 +8389,22 @@ function attributeGraph(machine, doc, options = {}) {
|
|
|
7873
8389
|
edges: b.edges,
|
|
7874
8390
|
graph,
|
|
7875
8391
|
unknown: b.edges.filter((e) => e.from === void 0),
|
|
7876
|
-
cycles: cycles(graph).map((cycle) =>
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
8392
|
+
cycles: cycles(graph).map((cycle) => {
|
|
8393
|
+
const found = {
|
|
8394
|
+
vertices: cycle.nodes,
|
|
8395
|
+
certain: cycle.certain
|
|
8396
|
+
};
|
|
8397
|
+
return allCircular(b, cycle.nodes) ? {
|
|
8398
|
+
...found,
|
|
8399
|
+
circular: true
|
|
8400
|
+
} : found;
|
|
8401
|
+
})
|
|
7880
8402
|
};
|
|
7881
8403
|
}
|
|
8404
|
+
/** This function tells if each vertex of a list is a circular attribute (D113). */
|
|
8405
|
+
function allCircular(b, ids) {
|
|
8406
|
+
return ids.every((id) => b.vertices.get(id)?.circular === true);
|
|
8407
|
+
}
|
|
7882
8408
|
function vertexText(vertex) {
|
|
7883
8409
|
return `${vertex.attribute} of ${vertex.type} at [${vertex.place.join(", ")}]`;
|
|
7884
8410
|
}
|
|
@@ -7888,6 +8414,9 @@ function vertexText(vertex) {
|
|
|
7888
8414
|
* certain cycle that these reads reach always gives `#CYCLE!`. The issue gets the place of the read that closes the
|
|
7889
8415
|
* cycle in the order of the run, and the reason of the run. A cycle through a lazy read or an unknown read is not an issue.
|
|
7890
8416
|
*
|
|
8417
|
+
* A cycle whose attributes are each circular gives a fixed point, thus it is not an issue (TREE.CIRCULAR-CHECK). In a
|
|
8418
|
+
* cycle with a plain attribute, the run gives `#CYCLE!` at the first read that closes a chain with a plain attribute.
|
|
8419
|
+
*
|
|
7891
8420
|
* @param machine - The world and the code of the evaluators.
|
|
7892
8421
|
* @param doc - The document, as JSON.
|
|
7893
8422
|
* @param options - The type of a bare root value.
|
|
@@ -7898,7 +8427,7 @@ function cycleIssues(machine, doc, options) {
|
|
|
7898
8427
|
const graph = nodeGraph(b);
|
|
7899
8428
|
const certain = /* @__PURE__ */ new Map();
|
|
7900
8429
|
cycles(graph).forEach((cycle, index) => {
|
|
7901
|
-
if (cycle.certain) for (const id of cycle.nodes) certain.set(id, index);
|
|
8430
|
+
if (cycle.certain && !allCircular(b, cycle.nodes)) for (const id of cycle.nodes) certain.set(id, index);
|
|
7902
8431
|
});
|
|
7903
8432
|
if (certain.size === 0) return [];
|
|
7904
8433
|
const eager = /* @__PURE__ */ new Map();
|
|
@@ -7946,9 +8475,11 @@ function cycleIssues(machine, doc, options) {
|
|
|
7946
8475
|
if (seen === "done") continue;
|
|
7947
8476
|
const cycle = certain.get(from);
|
|
7948
8477
|
if (cycle === void 0 || reported.has(cycle)) continue;
|
|
7949
|
-
reported.add(cycle);
|
|
7950
8478
|
const first = stack.findIndex((frame) => frame.id === from);
|
|
7951
|
-
const
|
|
8479
|
+
const ids = stack.slice(first).map((frame) => frame.id);
|
|
8480
|
+
if (allCircular(b, ids)) continue;
|
|
8481
|
+
reported.add(cycle);
|
|
8482
|
+
const chain = [...ids, from].map((id) => vertexText(b.vertices.get(id)));
|
|
7952
8483
|
out.push({
|
|
7953
8484
|
code: "#CYCLE!",
|
|
7954
8485
|
place: e.place,
|
|
@@ -8592,7 +9123,8 @@ function equationIssues(machine, kind, kindText, options, state) {
|
|
|
8592
9123
|
}
|
|
8593
9124
|
/**
|
|
8594
9125
|
* This function finds the issues of the contract of a tree kind (TREE.CHECK-RESERVED). An attribute with the name of a
|
|
8595
|
-
* structural step gets no read. A tree kind of the run side contradicts the evaluation on demand of its attributes.
|
|
9126
|
+
* structural step gets no read. A tree kind of the run side contradicts the evaluation on demand of its attributes. A
|
|
9127
|
+
* declaration `circular` without a start value or with a bad limit gives `#VALUE!` (TREE.CIRCULAR-CHECK).
|
|
8596
9128
|
*/
|
|
8597
9129
|
function contractIssues(kind, kindText) {
|
|
8598
9130
|
const out = [];
|
|
@@ -8609,6 +9141,14 @@ function contractIssues(kind, kindText) {
|
|
|
8609
9141
|
reason: `The kind ${kindText} gives the reserved attribute tree.${table}.${name}: ${why}.`
|
|
8610
9142
|
});
|
|
8611
9143
|
}
|
|
9144
|
+
for (const [name, declared] of Object.entries(tree?.attributes ?? {})) {
|
|
9145
|
+
const rule = declared.circular === void 0 ? void 0 : circularOf(declared.circular);
|
|
9146
|
+
if (rule !== void 0 && !rule.ok) out.push({
|
|
9147
|
+
code: "#VALUE!",
|
|
9148
|
+
place: [],
|
|
9149
|
+
reason: `The kind ${kindText} has a bad declaration tree.attributes.${name}.circular. ${rule.reason}`
|
|
9150
|
+
});
|
|
9151
|
+
}
|
|
8612
9152
|
if (kind.side === "run") out.push({
|
|
8613
9153
|
code: "#CALC!",
|
|
8614
9154
|
place: [],
|
|
@@ -12124,31 +12664,146 @@ const REF_RUNGS = [
|
|
|
12124
12664
|
return declared?.default === void 0 ? void 0 : equationOf(node, declared.default, {});
|
|
12125
12665
|
}]
|
|
12126
12666
|
];
|
|
12127
|
-
/** TREE.
|
|
12667
|
+
/** TREE.LADDER: the answer of the first rung that answers, or `undefined`. */
|
|
12668
|
+
function ladderOf(node, name) {
|
|
12669
|
+
for (const [, rung] of REF_RUNGS) {
|
|
12670
|
+
const result = rung(node, name);
|
|
12671
|
+
if (result !== void 0) return result;
|
|
12672
|
+
}
|
|
12673
|
+
}
|
|
12674
|
+
/** The open fixed point of the reference, or `undefined`. */
|
|
12675
|
+
let refPoint;
|
|
12676
|
+
/** TREE.CIRCULAR-CHECK: the declaration `circular` of an attribute of a node, or `undefined`. */
|
|
12677
|
+
function circularRuleOf(node, name) {
|
|
12678
|
+
const declared = termIn(node.contract?.attributes, name)?.circular;
|
|
12679
|
+
return declared === void 0 ? void 0 : circularOf(declared);
|
|
12680
|
+
}
|
|
12681
|
+
/** TREE.CIRCULAR: two approximations are the same when two values are equal (DOC.EQUALS) or two errors have one code and reason. */
|
|
12682
|
+
function sameApproximation(a, b) {
|
|
12683
|
+
if (a === void 0 || b === void 0) return a === b;
|
|
12684
|
+
if (a.ok && b.ok) return docEquals(a.value, b.value);
|
|
12685
|
+
if (!a.ok && !b.ok) return a.error.code === b.error.code && a.error.reason === b.error.reason;
|
|
12686
|
+
return false;
|
|
12687
|
+
}
|
|
12688
|
+
/** TREE.CIRCULAR: the attributes above the leader read an approximation, thus their results are not final. */
|
|
12689
|
+
function readApproximation(point) {
|
|
12690
|
+
for (const entry of inEvaluation.slice(point.depth + 1)) entry.tentative = true;
|
|
12691
|
+
}
|
|
12692
|
+
function approximationsOf(point, node) {
|
|
12693
|
+
const known = point.values.get(node);
|
|
12694
|
+
if (known !== void 0) return known;
|
|
12695
|
+
const made = /* @__PURE__ */ new Map();
|
|
12696
|
+
point.values.set(node, made);
|
|
12697
|
+
return made;
|
|
12698
|
+
}
|
|
12699
|
+
/**
|
|
12700
|
+
* TREE.CIRCULAR and TREE.CIRCULAR-LIMIT: the rounds of the leader of a fixed point. A round without a new value ends
|
|
12701
|
+
* the rounds, and each circular attribute of that round keeps its value. An error ends the rounds at once. The last
|
|
12702
|
+
* round of the limit gives `#CYCLE!` at the place of the node of the leader.
|
|
12703
|
+
*/
|
|
12704
|
+
function fixedPointOf(node, name, rule) {
|
|
12705
|
+
const point = {
|
|
12706
|
+
depth: inEvaluation.length - 1,
|
|
12707
|
+
round: 0,
|
|
12708
|
+
changed: false,
|
|
12709
|
+
values: /* @__PURE__ */ new Map()
|
|
12710
|
+
};
|
|
12711
|
+
refPoint = point;
|
|
12712
|
+
try {
|
|
12713
|
+
let before = ok(rule.start);
|
|
12714
|
+
for (;;) {
|
|
12715
|
+
point.round += 1;
|
|
12716
|
+
point.changed = false;
|
|
12717
|
+
const result = ladderOf(node, name);
|
|
12718
|
+
if (result !== void 0 && !result.ok) return result;
|
|
12719
|
+
if (!sameApproximation(before, result)) point.changed = true;
|
|
12720
|
+
before = result;
|
|
12721
|
+
approximationsOf(point, node).set(name, {
|
|
12722
|
+
result,
|
|
12723
|
+
round: point.round
|
|
12724
|
+
});
|
|
12725
|
+
if (!point.changed) {
|
|
12726
|
+
for (const [member, values] of point.values) for (const [key, value] of values) if (value.round === point.round && value.result !== void 0) member.values.set(key, value.result);
|
|
12727
|
+
return result;
|
|
12728
|
+
}
|
|
12729
|
+
if (point.round >= rule.limit) return fail("#CYCLE!", `The cycle of the circular attribute ${nameOfAttribute(node, name)} does not become stable within its limit of ${rule.limit} ${rule.limit === 1 ? "round" : "rounds"}.`, { place: node.place });
|
|
12730
|
+
}
|
|
12731
|
+
} finally {
|
|
12732
|
+
refPoint = void 0;
|
|
12733
|
+
}
|
|
12734
|
+
}
|
|
12735
|
+
/** TREE.CIRCULAR: a circular attribute in the open fixed point of another leader evaluates one time in each round. */
|
|
12736
|
+
function roundOf(point, node, name, rule) {
|
|
12737
|
+
let value = point.values.get(node)?.get(name);
|
|
12738
|
+
if (value === void 0 || value.round !== point.round) {
|
|
12739
|
+
node.pending.add(name);
|
|
12740
|
+
inEvaluation.push({
|
|
12741
|
+
node,
|
|
12742
|
+
name,
|
|
12743
|
+
circular: true,
|
|
12744
|
+
tentative: false
|
|
12745
|
+
});
|
|
12746
|
+
let result;
|
|
12747
|
+
try {
|
|
12748
|
+
result = ladderOf(node, name);
|
|
12749
|
+
} finally {
|
|
12750
|
+
node.pending.delete(name);
|
|
12751
|
+
inEvaluation.pop();
|
|
12752
|
+
}
|
|
12753
|
+
if (!sameApproximation(value === void 0 ? ok(rule.start) : value.result, result)) point.changed = true;
|
|
12754
|
+
value = {
|
|
12755
|
+
result,
|
|
12756
|
+
round: point.round
|
|
12757
|
+
};
|
|
12758
|
+
approximationsOf(point, node).set(name, value);
|
|
12759
|
+
}
|
|
12760
|
+
readApproximation(point);
|
|
12761
|
+
return value.result;
|
|
12762
|
+
}
|
|
12763
|
+
/**
|
|
12764
|
+
* TREE.ONCE, TREE.MEMO, TREE.CYCLE and TREE.CIRCULAR: the result of one attribute, or `undefined` when no rung of its
|
|
12765
|
+
* ladder answers. A result that read an approximation of an open fixed point is not kept.
|
|
12766
|
+
*/
|
|
12128
12767
|
function attributeOf(node, name) {
|
|
12129
12768
|
const done = node.values.get(name);
|
|
12130
12769
|
if (done !== void 0) return done;
|
|
12131
12770
|
if (node.pending.has(name)) {
|
|
12132
12771
|
const from = inEvaluation.findIndex((entry) => entry.node === node && entry.name === name);
|
|
12772
|
+
const point = refPoint;
|
|
12773
|
+
if (point !== void 0 && from >= point.depth && inEvaluation.slice(from).every((entry) => entry.circular)) {
|
|
12774
|
+
readApproximation(point);
|
|
12775
|
+
const kept = point.values.get(node)?.get(name);
|
|
12776
|
+
if (kept !== void 0) return kept.result;
|
|
12777
|
+
const rule = circularRuleOf(node, name);
|
|
12778
|
+
return ok(rule?.ok === true ? rule.start : null);
|
|
12779
|
+
}
|
|
12133
12780
|
return fail("#CYCLE!", `The attributes form a cycle: ${[...from < 0 ? [nameOfAttribute(node, name)] : inEvaluation.slice(from).map((entry) => nameOfAttribute(entry.node, entry.name)), nameOfAttribute(node, name)].join(" -> ")}.`);
|
|
12134
12781
|
}
|
|
12135
|
-
node
|
|
12136
|
-
|
|
12782
|
+
const rule = circularRuleOf(node, name);
|
|
12783
|
+
if (rule !== void 0 && !rule.ok) {
|
|
12784
|
+
const bad = fail("#VALUE!", `The attribute ${nameOfAttribute(node, name)} has a bad declaration circular. ${rule.reason}`);
|
|
12785
|
+
node.values.set(name, bad);
|
|
12786
|
+
return bad;
|
|
12787
|
+
}
|
|
12788
|
+
const point = refPoint;
|
|
12789
|
+
if (point !== void 0 && rule !== void 0) return roundOf(point, node, name, rule);
|
|
12790
|
+
const entry = {
|
|
12137
12791
|
node,
|
|
12138
|
-
name
|
|
12139
|
-
|
|
12792
|
+
name,
|
|
12793
|
+
circular: rule !== void 0,
|
|
12794
|
+
tentative: false
|
|
12795
|
+
};
|
|
12796
|
+
node.pending.add(name);
|
|
12797
|
+
inEvaluation.push(entry);
|
|
12798
|
+
let result;
|
|
12140
12799
|
try {
|
|
12141
|
-
|
|
12142
|
-
const result = rung(node, name);
|
|
12143
|
-
if (result === void 0) continue;
|
|
12144
|
-
node.values.set(name, result);
|
|
12145
|
-
return result;
|
|
12146
|
-
}
|
|
12147
|
-
return;
|
|
12800
|
+
result = rule === void 0 ? ladderOf(node, name) : fixedPointOf(node, name, rule);
|
|
12148
12801
|
} finally {
|
|
12149
12802
|
node.pending.delete(name);
|
|
12150
12803
|
inEvaluation.pop();
|
|
12151
12804
|
}
|
|
12805
|
+
if (!entry.tentative && result !== void 0) node.values.set(name, result);
|
|
12806
|
+
return result;
|
|
12152
12807
|
}
|
|
12153
12808
|
/** TREE.RESULT: the JSON of a node. `open` holds the nodes and the containers whose JSON is in progress. */
|
|
12154
12809
|
function jsonOfNode(node, self, open) {
|
|
@@ -16814,6 +17469,6 @@ function samplesAt(values, closed, depth) {
|
|
|
16814
17469
|
function typeFromSamples(values, options = {}) {
|
|
16815
17470
|
return samplesAt(values, options.closed === true, 0);
|
|
16816
17471
|
}
|
|
16817
|
-
export { ATTRIBUTE_RUNGS, CAPS_TYPE, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, WORLD_SYNTAX, agree, agreeRun, agreeSchedule, ask, attributeGraph, attributeTrace, attributeVertexId, capKeys, check, checkLatent, checkSteps, checkWorld, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeLog, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeLog, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placeOf, placed, planner, prepare, print, printReads, random, recorder, ref, reference, referenceRun, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stored, syntaxFormat, tag, thrown, toJson, treeNodeEvaluator, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
|
|
17472
|
+
export { ATTRIBUTE_RUNGS, CAPS_TYPE, CIRCULAR_LIMIT, CONSTRUCTS, ENVELOPE_KEYS, Failure, Held, JOURNAL, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, WORLD_SYNTAX, agree, agreeRun, agreeSchedule, ask, attributeGraph, attributeTrace, attributeVertexId, capKeys, check, checkLatent, checkStep, checkSteps, checkWorld, circularOf, classify, components, conforms, context, cycles, dataEdges, decodeDomain, decodeLog, decodeValue, derefStep, describe, docEquals, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeLog, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isStep, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placeOf, placed, planner, prepare, print, printReads, random, recorder, ref, reference, referenceRun, refsOf, replayer, resolve, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stepLadder, stepOf, stepRank, stored, syntaxFormat, tag, thrown, toJson, treeNodeEvaluator, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
|
|
16818
17473
|
|
|
16819
17474
|
//# sourceMappingURL=index.js.map
|