@mark1russell7/coeval 0.26.0 → 0.27.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/build/index.d.ts +25 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +196 -79
- package/build/index.js.map +1 -1
- package/package.json +4 -1
- package/src/errors.ts +1 -0
- package/src/evaluate.ts +113 -14
- package/src/formula.ts +7 -4
- package/src/generate.ts +4 -9
- package/src/index.ts +1 -0
- package/src/merge.ts +1 -1
- package/src/reference.ts +93 -38
- package/src/stdlib.ts +39 -14
package/src/stdlib.ts
CHANGED
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
type Result,
|
|
10
10
|
type RunPart,
|
|
11
11
|
type Runner,
|
|
12
|
+
type Runtime,
|
|
12
13
|
type Steps,
|
|
14
|
+
NODE_WORK,
|
|
13
15
|
RUNTIME,
|
|
14
16
|
evaluateWithin,
|
|
15
17
|
fail,
|
|
@@ -245,11 +247,20 @@ function isNumber(value: unknown): value is number {
|
|
|
245
247
|
function allValues(results: readonly Result[]): { readonly values: readonly unknown[] } | { readonly error: Result } {
|
|
246
248
|
const errors = results.filter((result) => !result.ok);
|
|
247
249
|
const first = errors[0];
|
|
250
|
+
// D190: the test of the errors comes before the values. With the values first, a case of the bench took 1.2 to 1.4
|
|
251
|
+
// times the time in the Maglev tier of V8, in probe 4 of K216.
|
|
248
252
|
if (first !== undefined && errors.length === 1) return { error: first };
|
|
249
|
-
if (errors.length > 1) return { error:
|
|
253
|
+
if (errors.length > 1) return { error: partsFailed(errors) };
|
|
250
254
|
return { values: results.map((result) => (result.ok ? result.value : null)) };
|
|
251
255
|
}
|
|
252
256
|
|
|
257
|
+
/** This function gives the error of more than one failed part. */
|
|
258
|
+
function partsFailed(errors: readonly Result[]): Result {
|
|
259
|
+
// WORK.LIMIT: after the limit each counted part fails, and the code of the limit stays the code of the result.
|
|
260
|
+
const stopped = errors.find((result) => !result.ok && result.error.code === "#WORK!");
|
|
261
|
+
return stopped ?? fail("#ARGS", `${errors.length} parts failed.`);
|
|
262
|
+
}
|
|
263
|
+
|
|
253
264
|
function numbers(values: readonly unknown[], kind: string): readonly number[] | Result {
|
|
254
265
|
const bad = values.findIndex((value) => !isNumber(value));
|
|
255
266
|
if (bad !== -1) return fail("#VALUE!", `Part ${bad} of ${kind} is not a number.`);
|
|
@@ -379,7 +390,7 @@ interface NodeState extends LoopNode {
|
|
|
379
390
|
* @param path - The nodes and the containers whose JSON is in progress. A node on it gives `#CYCLE!`.
|
|
380
391
|
* @returns The JSON, or the first error.
|
|
381
392
|
*/
|
|
382
|
-
json(path:
|
|
393
|
+
json(path: JsonWalk): Result;
|
|
383
394
|
}
|
|
384
395
|
|
|
385
396
|
/** The answer of the ladder for one attribute of one node (TREE.CELL, D108). */
|
|
@@ -887,24 +898,35 @@ function putField(target: Record<string, unknown>, key: string, value: unknown):
|
|
|
887
898
|
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
|
|
888
899
|
}
|
|
889
900
|
|
|
901
|
+
/**
|
|
902
|
+
* The record of a walk for JSON. A node, list or record has `true` while its JSON is in progress, and `false` after
|
|
903
|
+
* that. Thus a value in the map with `false` is a second visit (WORK.SHARED).
|
|
904
|
+
*/
|
|
905
|
+
type JsonWalk = Map<object, boolean>;
|
|
906
|
+
|
|
890
907
|
/**
|
|
891
908
|
* 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
|
|
892
909
|
* it holds a node. A container that holds itself stays as it is.
|
|
893
910
|
*/
|
|
894
|
-
function plainOf(value: unknown, path:
|
|
911
|
+
function plainOf(value: unknown, path: JsonWalk, runtime: Runtime | undefined): Result {
|
|
912
|
+
if (typeof value !== "object" || value === null) return ok(value);
|
|
895
913
|
const state = stateOf(value);
|
|
914
|
+
const open = path.get(value);
|
|
896
915
|
if (state !== undefined) {
|
|
897
|
-
if (
|
|
916
|
+
if (open === true) return fail("#CYCLE!", `The JSON of the node ${state.type} at [${state.place.join(", ")}] holds the node itself.`);
|
|
917
|
+
if (open === false) runtime?.spend?.(1);
|
|
898
918
|
return state.json(path);
|
|
899
919
|
}
|
|
900
|
-
if (
|
|
920
|
+
if (open === true || isHostThing(value)) return ok(value);
|
|
901
921
|
if (!Array.isArray(value) && !isRecord(value)) return ok(value);
|
|
902
|
-
|
|
922
|
+
// WORK.SHARED: a second visit of a container counts one point, because a chain of shared containers doubles the walk.
|
|
923
|
+
if (open === false) runtime?.spend?.(1);
|
|
924
|
+
path.set(value, true);
|
|
903
925
|
try {
|
|
904
926
|
if (Array.isArray(value)) {
|
|
905
927
|
let list: unknown[] | undefined;
|
|
906
928
|
for (let index = 0; index < value.length; index += 1) {
|
|
907
|
-
const item = plainOf(value[index], path);
|
|
929
|
+
const item = plainOf(value[index], path, runtime);
|
|
908
930
|
if (!item.ok) return item;
|
|
909
931
|
if (item.value !== value[index]) (list ??= [...(value as readonly unknown[])])[index] = item.value;
|
|
910
932
|
}
|
|
@@ -913,7 +935,7 @@ function plainOf(value: unknown, path: Set<object>): Result {
|
|
|
913
935
|
const record = value as Readonly<Record<string, unknown>>;
|
|
914
936
|
let out: Record<string, unknown> | undefined;
|
|
915
937
|
for (const key of Object.keys(record)) {
|
|
916
|
-
const item = plainOf(record[key], path);
|
|
938
|
+
const item = plainOf(record[key], path, runtime);
|
|
917
939
|
if (!item.ok) return item;
|
|
918
940
|
if (item.value === record[key]) continue;
|
|
919
941
|
if (out === undefined) {
|
|
@@ -924,7 +946,7 @@ function plainOf(value: unknown, path: Set<object>): Result {
|
|
|
924
946
|
}
|
|
925
947
|
return ok(out ?? value);
|
|
926
948
|
} finally {
|
|
927
|
-
path.
|
|
949
|
+
path.set(value, false);
|
|
928
950
|
}
|
|
929
951
|
}
|
|
930
952
|
|
|
@@ -1323,19 +1345,19 @@ function treeThing(input: EvalInput, place: readonly Key[], build: TreeBuild, ou
|
|
|
1323
1345
|
return cell.by === undefined ? { attribute: key, rungs } : { attribute: key, rungs, by: cell.by };
|
|
1324
1346
|
},
|
|
1325
1347
|
json(path) {
|
|
1326
|
-
path.
|
|
1348
|
+
path.set(node, true);
|
|
1327
1349
|
try {
|
|
1328
1350
|
const out: Record<string, unknown> = {};
|
|
1329
1351
|
for (const part of parts) {
|
|
1330
1352
|
const result = read(part.key) as Result;
|
|
1331
1353
|
if (!result.ok) return result;
|
|
1332
|
-
const value = plainOf(result.value, path);
|
|
1354
|
+
const value = plainOf(result.value, path, runtime);
|
|
1333
1355
|
if (!value.ok) return value;
|
|
1334
1356
|
putField(out, part.key, value.value);
|
|
1335
1357
|
}
|
|
1336
1358
|
return ok(out);
|
|
1337
1359
|
} finally {
|
|
1338
|
-
path.
|
|
1360
|
+
path.set(node, false);
|
|
1339
1361
|
}
|
|
1340
1362
|
},
|
|
1341
1363
|
};
|
|
@@ -1359,7 +1381,7 @@ function treeThing(input: EvalInput, place: readonly Key[], build: TreeBuild, ou
|
|
|
1359
1381
|
return cell !== undefined && !cell.busy && cell.result !== undefined;
|
|
1360
1382
|
},
|
|
1361
1383
|
toJson(): unknown {
|
|
1362
|
-
const result = state.json(new
|
|
1384
|
+
const result = state.json(new Map());
|
|
1363
1385
|
if (!result.ok) throw new Failure(result.error);
|
|
1364
1386
|
return result.value;
|
|
1365
1387
|
},
|
|
@@ -1404,6 +1426,9 @@ const NO_HOPS = /* @__PURE__ */ Object.freeze({ hops: false });
|
|
|
1404
1426
|
* The argument `host` gives the memo cells of the host (TREE.CELL), or `undefined` for the plain memo of each node.
|
|
1405
1427
|
*/
|
|
1406
1428
|
function treeNode(input: EvalInput, host: AttributeCell | undefined): Result {
|
|
1429
|
+
const runtime = (input as InputWithRuntime)[RUNTIME];
|
|
1430
|
+
// WORK.NODE: a node that an equation makes counts. A node of the text of the document does not.
|
|
1431
|
+
if (runtime?.nested === true) runtime.spend?.(NODE_WORK);
|
|
1407
1432
|
const found = input.resolve(SELF_PATH, NO_HOPS);
|
|
1408
1433
|
const outer = found.ok ? found.thing : undefined;
|
|
1409
1434
|
const above = stateOf(outer);
|
|
@@ -1415,7 +1440,7 @@ function treeNode(input: EvalInput, host: AttributeCell | undefined): Result {
|
|
|
1415
1440
|
build.open = true;
|
|
1416
1441
|
const root = treeThing(input, place, build, outer, host);
|
|
1417
1442
|
try {
|
|
1418
|
-
return (stateOf(root) as NodeState).json(new
|
|
1443
|
+
return (stateOf(root) as NodeState).json(new Map());
|
|
1419
1444
|
} finally {
|
|
1420
1445
|
build.open = false;
|
|
1421
1446
|
}
|