@calcit/procs 0.29.0-alpha.3 → 0.29.0-alpha.30
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/lib/calcit-data.d.mts +1 -0
- package/lib/calcit-data.mjs +47 -6
- package/lib/calcit.procs.d.mts +12 -7
- package/lib/calcit.procs.mjs +214 -101
- package/lib/js-cirru.d.mts +1 -1
- package/lib/js-cirru.mjs +9 -2
- package/lib/js-primes.d.mts +2 -0
- package/lib/js-primes.mjs +88 -1
- package/lib/js-ref.d.mts +3 -0
- package/lib/js-ref.mjs +4 -1
- package/lib/js-struct-def.d.mts +5 -3
- package/lib/js-struct-def.mjs +14 -6
- package/lib/js-struct-value.mjs +55 -88
- package/lib/js-type-form.d.mts +2 -0
- package/lib/js-type-form.mjs +188 -0
- package/lib/package.json +4 -3
- package/package.json +4 -3
package/lib/calcit.procs.mjs
CHANGED
|
@@ -3,7 +3,7 @@ export const calcit_version = pkg.version;
|
|
|
3
3
|
export const calcit_package_json = pkg;
|
|
4
4
|
import { parse } from "@cirru/parser.ts";
|
|
5
5
|
import { writeCirruCode } from "@cirru/writer.ts";
|
|
6
|
-
import { CalcitSymbol, CalcitTag, CalcitRecur, castTag, newTag, refsRegistry, toString, getStringName, compareTagNames, _$n__$e_, hashFunction, } from "./calcit-data.mjs";
|
|
6
|
+
import { CalcitSymbol, CalcitTag, CalcitRecur, castTag, newTag, refsRegistry, toString, format_calcit_number, getStringName, compareTagNames, _$n__$e_, hashFunction, } from "./calcit-data.mjs";
|
|
7
7
|
import { CalcitRef, atom } from "./js-ref.mjs";
|
|
8
8
|
import { CalcitStructValue } from "./js-struct-value.mjs";
|
|
9
9
|
import { CalcitImpl } from "./js-impl.mjs";
|
|
@@ -508,7 +508,7 @@ export let defatom = (path, x) => {
|
|
|
508
508
|
refsRegistry.set(path, v);
|
|
509
509
|
return v;
|
|
510
510
|
};
|
|
511
|
-
export { atom } from "./js-ref.mjs";
|
|
511
|
+
export { atom, ref } from "./js-ref.mjs";
|
|
512
512
|
export let peekDefatom = (path) => {
|
|
513
513
|
return refsRegistry.get(path);
|
|
514
514
|
};
|
|
@@ -817,7 +817,7 @@ export let _$n_struct_$o_impl_traits = function (xs, ...traits) {
|
|
|
817
817
|
throw new Error("&struct:impl-traits expected a struct value");
|
|
818
818
|
const impls = traits.map((trait) => coerce_impl(trait, "&struct:impl-traits"));
|
|
819
819
|
checkRequiredTraitImpls(xs.structRef.impls.concat(impls), "&struct:impl-traits");
|
|
820
|
-
const nextStruct = new CalcitStructDef(xs.name, xs.fields, xs.structRef.fieldTypes, xs.structRef.impls.concat(impls), xs.structRef.definitionRef);
|
|
820
|
+
const nextStruct = new CalcitStructDef(xs.name, xs.fields, xs.structRef.fieldTypes, xs.structRef.impls.concat(impls), xs.structRef.definitionRef, xs.structRef.fieldValidators);
|
|
821
821
|
return new CalcitStructValue(xs.name, xs.fields, xs.values, nextStruct);
|
|
822
822
|
};
|
|
823
823
|
export let _$n_struct_def_$o_impl_traits = function (xs, ...traits) {
|
|
@@ -828,7 +828,7 @@ export let _$n_struct_def_$o_impl_traits = function (xs, ...traits) {
|
|
|
828
828
|
const addedImpls = traits.map((trait) => coerce_impl(trait, "&struct-def:impl-traits"));
|
|
829
829
|
const baseImpls = xs.impls ?? [];
|
|
830
830
|
checkRequiredTraitImpls(baseImpls.concat(addedImpls), "&struct-def:impl-traits");
|
|
831
|
-
return new CalcitStructDef(xs.name, xs.fields, xs.fieldTypes, baseImpls.concat(addedImpls), xs.definitionRef);
|
|
831
|
+
return new CalcitStructDef(xs.name, xs.fields, xs.fieldTypes, baseImpls.concat(addedImpls), xs.definitionRef, xs.fieldValidators);
|
|
832
832
|
};
|
|
833
833
|
export let _$n_enum_def_$o_impl_traits = function (xs, ...traits) {
|
|
834
834
|
if (traits.length < 1)
|
|
@@ -920,7 +920,7 @@ export let reset_$x_ = (a, v) => {
|
|
|
920
920
|
});
|
|
921
921
|
return v;
|
|
922
922
|
};
|
|
923
|
-
export let
|
|
923
|
+
export let add_watch_$x_ = (a, k, f) => {
|
|
924
924
|
if (!(a instanceof CalcitRef)) {
|
|
925
925
|
throw new Error("Expected ref for add-watch!");
|
|
926
926
|
}
|
|
@@ -930,14 +930,25 @@ export let add_watch = (a, k, f) => {
|
|
|
930
930
|
if (!(typeof f === "function")) {
|
|
931
931
|
throw new Error("Expected watcher function");
|
|
932
932
|
}
|
|
933
|
+
for (const listenerKey of a.listeners.keys()) {
|
|
934
|
+
if (listenerKey instanceof CalcitTag && listenerKey.value === k.value) {
|
|
935
|
+
throw new Error(`add-watch! failed: listener with key \`${k.value}\` already existed`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
933
938
|
a.listeners.set(k, f);
|
|
934
939
|
};
|
|
935
940
|
export let remove_watch = (a, k) => {
|
|
936
|
-
|
|
937
|
-
|
|
941
|
+
for (const listenerKey of a.listeners.keys()) {
|
|
942
|
+
if (listenerKey instanceof CalcitTag && listenerKey.value === k.value) {
|
|
943
|
+
a.listeners.delete(listenerKey);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
938
946
|
}
|
|
947
|
+
throw new Error(`remove-watch failed: listener with key \`${k.value}\` not found`);
|
|
939
948
|
};
|
|
940
|
-
|
|
949
|
+
// Same eager range limit as native `MAX_RANGE_LEN` and WASM, 2^24 elements (#1852).
|
|
950
|
+
const MAX_RANGE_LENGTH = 2 ** 24;
|
|
951
|
+
const rangeTooLarge = () => new Error(`&list:range result is too large: more than ${MAX_RANGE_LENGTH} elements`);
|
|
941
952
|
export let range = (n, m, step = 1) => {
|
|
942
953
|
const base = m == null ? 0 : n;
|
|
943
954
|
const bound = m == null ? n : m;
|
|
@@ -955,11 +966,13 @@ export let range = (n, m, step = 1) => {
|
|
|
955
966
|
? Math.ceil(Math.abs(base) / Math.abs(step) + Math.abs(bound) / Math.abs(step))
|
|
956
967
|
: Math.ceil((bound - base) / step);
|
|
957
968
|
if (!Number.isFinite(estimatedLength) || estimatedLength > MAX_RANGE_LENGTH) {
|
|
958
|
-
throw
|
|
969
|
+
throw rangeTooLarge();
|
|
959
970
|
}
|
|
960
971
|
const result = [];
|
|
961
972
|
if (step > 0) {
|
|
962
973
|
for (let value = base; value < bound;) {
|
|
974
|
+
if (result.length >= MAX_RANGE_LENGTH)
|
|
975
|
+
throw rangeTooLarge();
|
|
963
976
|
result.push(value);
|
|
964
977
|
const next = value + step;
|
|
965
978
|
if (next === value) {
|
|
@@ -970,6 +983,8 @@ export let range = (n, m, step = 1) => {
|
|
|
970
983
|
}
|
|
971
984
|
else {
|
|
972
985
|
for (let value = base; value > bound;) {
|
|
986
|
+
if (result.length >= MAX_RANGE_LENGTH)
|
|
987
|
+
throw rangeTooLarge();
|
|
973
988
|
result.push(value);
|
|
974
989
|
const next = value + step;
|
|
975
990
|
if (next === value) {
|
|
@@ -1149,11 +1164,12 @@ export let _$n_list_$o_slice = (xs, from, to) => {
|
|
|
1149
1164
|
if (to == null) {
|
|
1150
1165
|
to = size;
|
|
1151
1166
|
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1167
|
+
// Bounds are integers with 0 <= from <= to <= size, as in native (docs/data/number.md).
|
|
1168
|
+
if (!Number.isInteger(from) || !Number.isInteger(to) || from < 0 || from > to || to > size) {
|
|
1169
|
+
throw new Error(`&list:slice expected integer bounds within 0..${size}, but received: ${from} ${to}`);
|
|
1154
1170
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1171
|
+
if (from === to) {
|
|
1172
|
+
return new CalcitSliceList([]);
|
|
1157
1173
|
}
|
|
1158
1174
|
return xs.slice(from, to);
|
|
1159
1175
|
};
|
|
@@ -1200,10 +1216,11 @@ export let _$n__ = (a, b) => {
|
|
|
1200
1216
|
export let _$n__SLSH_ = (a, b) => {
|
|
1201
1217
|
return a / b;
|
|
1202
1218
|
};
|
|
1203
|
-
/** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`.
|
|
1219
|
+
/** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`.
|
|
1220
|
+
* Operands in the error use the native number text (`inf`, expanded exponents) so messages match native. */
|
|
1204
1221
|
export let _$n_number_$o_rem = (a, b) => {
|
|
1205
1222
|
if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b)) {
|
|
1206
|
-
throw new Error(`&number:rem requires safe integers, but received: ${
|
|
1223
|
+
throw new Error(`&number:rem requires safe integers, but received: ${format_calcit_number(a)} ${format_calcit_number(b)}`);
|
|
1207
1224
|
}
|
|
1208
1225
|
if (b === 0) {
|
|
1209
1226
|
throw new Error("&number:rem divisor must not be zero");
|
|
@@ -1350,13 +1367,19 @@ export let cos = (n) => {
|
|
|
1350
1367
|
return Math.cos(n);
|
|
1351
1368
|
};
|
|
1352
1369
|
export let pow = (n, m) => {
|
|
1370
|
+
// IEEE 754 pow, which Math.pow breaks: pow(1, y) is 1 for any y, pow(-1, ±Infinity) is 1.
|
|
1371
|
+
if (n === 1 || (n === -1 && (m === Infinity || m === -Infinity))) {
|
|
1372
|
+
return 1;
|
|
1373
|
+
}
|
|
1353
1374
|
return Math.pow(n, m);
|
|
1354
1375
|
};
|
|
1355
1376
|
export let ceil = (n) => {
|
|
1356
1377
|
return Math.ceil(n);
|
|
1357
1378
|
};
|
|
1358
1379
|
export let round = (n) => {
|
|
1359
|
-
|
|
1380
|
+
// Ties round away from zero; `Math.round` alone rounds them toward +Infinity.
|
|
1381
|
+
// Negating keeps `-0` for inputs such as -0.4.
|
|
1382
|
+
return n < 0 ? -Math.round(-n) : Math.round(n);
|
|
1360
1383
|
};
|
|
1361
1384
|
export let _$n_number_$o_fract = (n) => {
|
|
1362
1385
|
return n - Math.floor(n);
|
|
@@ -1418,7 +1441,12 @@ export let _$n_str_$o_replace = (x, y, z) => {
|
|
|
1418
1441
|
return x.replace(new RegExp(literalPattern, "gu"), () => z);
|
|
1419
1442
|
};
|
|
1420
1443
|
export let split = (xs, x) => {
|
|
1421
|
-
|
|
1444
|
+
// An empty delimiter splits by code point, matching native for astral characters.
|
|
1445
|
+
if (x === "") {
|
|
1446
|
+
return new CalcitSliceList(Array.from(xs));
|
|
1447
|
+
}
|
|
1448
|
+
// Drop empty pieces to match native and WASM: `split | |,` is an empty list.
|
|
1449
|
+
return new CalcitSliceList(xs.split(x).filter((piece) => piece !== ""));
|
|
1422
1450
|
};
|
|
1423
1451
|
export let split_lines = (xs) => {
|
|
1424
1452
|
return new CalcitSliceList(xs.split("\n"));
|
|
@@ -1504,6 +1532,10 @@ export let _$n_number_$o_format = (x, n) => {
|
|
|
1504
1532
|
return x.toFixed(n);
|
|
1505
1533
|
};
|
|
1506
1534
|
export let _$n_number_$o_display_by = (x, n) => {
|
|
1535
|
+
// a non-negative safe integer, printed exactly (docs/data/number.md)
|
|
1536
|
+
if (typeof x !== "number" || !Number.isSafeInteger(x) || x < 0) {
|
|
1537
|
+
throw new Error(`&number:display-by expected a non-negative safe integer, but received: ${x}`);
|
|
1538
|
+
}
|
|
1507
1539
|
switch (n) {
|
|
1508
1540
|
case 2:
|
|
1509
1541
|
return `0b${x.toString(2)}`;
|
|
@@ -1655,8 +1687,8 @@ export let to_lispy_string = (...args) => {
|
|
|
1655
1687
|
export let printable = (...args) => {
|
|
1656
1688
|
return args.map((x) => toString(x, false)).join(" ");
|
|
1657
1689
|
};
|
|
1658
|
-
//
|
|
1659
|
-
export let
|
|
1690
|
+
// monotonic milliseconds since app start
|
|
1691
|
+
export let monotonic_time_ms = () => {
|
|
1660
1692
|
if (inNodeJs) {
|
|
1661
1693
|
// uptime returns in seconds
|
|
1662
1694
|
return process.uptime() * 1000;
|
|
@@ -1675,37 +1707,6 @@ export let quit_$x_ = (code) => {
|
|
|
1675
1707
|
throw new Error(`quit!(${code})`);
|
|
1676
1708
|
}
|
|
1677
1709
|
};
|
|
1678
|
-
let format_calcit_number = (x) => {
|
|
1679
|
-
// Match native f64 Display, which expands finite exponents into decimal notation.
|
|
1680
|
-
if (Object.is(x, -0)) {
|
|
1681
|
-
return "-0";
|
|
1682
|
-
}
|
|
1683
|
-
if (x === Infinity) {
|
|
1684
|
-
return "inf";
|
|
1685
|
-
}
|
|
1686
|
-
if (x === -Infinity) {
|
|
1687
|
-
return "-inf";
|
|
1688
|
-
}
|
|
1689
|
-
const text = x.toString();
|
|
1690
|
-
const exponentIndex = text.indexOf("e");
|
|
1691
|
-
if (exponentIndex < 0) {
|
|
1692
|
-
return text;
|
|
1693
|
-
}
|
|
1694
|
-
const mantissa = text.slice(0, exponentIndex);
|
|
1695
|
-
const exponent = Number(text.slice(exponentIndex + 1));
|
|
1696
|
-
const sign = mantissa.startsWith("-") ? "-" : "";
|
|
1697
|
-
const unsigned = sign ? mantissa.slice(1) : mantissa;
|
|
1698
|
-
const decimalPoint = unsigned.indexOf(".");
|
|
1699
|
-
const decimalIndex = (decimalPoint < 0 ? unsigned.length : decimalPoint) + exponent;
|
|
1700
|
-
const digits = unsigned.replace(".", "");
|
|
1701
|
-
if (decimalIndex <= 0) {
|
|
1702
|
-
return `${sign}0.${"0".repeat(-decimalIndex)}${digits}`;
|
|
1703
|
-
}
|
|
1704
|
-
if (decimalIndex >= digits.length) {
|
|
1705
|
-
return `${sign}${digits}${"0".repeat(decimalIndex - digits.length)}`;
|
|
1706
|
-
}
|
|
1707
|
-
return `${sign}${digits.slice(0, decimalIndex)}.${digits.slice(decimalIndex)}`;
|
|
1708
|
-
};
|
|
1709
1710
|
export let turn_string = (x) => {
|
|
1710
1711
|
if (x === null) {
|
|
1711
1712
|
return "";
|
|
@@ -1941,6 +1942,64 @@ export let parse_cirru_edn = (code, options) => {
|
|
|
1941
1942
|
throw new Error(`Expected EDN in a single node, got ${nodes.length}`);
|
|
1942
1943
|
}
|
|
1943
1944
|
};
|
|
1945
|
+
/** Validate existing data without the decoder's map conversion or Option lifting. */
|
|
1946
|
+
export function validate_data_shape(input, graph) {
|
|
1947
|
+
if (graph.version !== 3)
|
|
1948
|
+
throw new Error(`validate-data-shape expected ABI version 3, got ${graph.version}`);
|
|
1949
|
+
const sameNominal = (actual, expected) => actual === expected || (actual != null && expected != null && expected.definitionRef != null &&
|
|
1950
|
+
actual.definitionRef === expected.definitionRef && actual.name.value === expected.name.value &&
|
|
1951
|
+
actual.fields.length === expected.fields.length && actual.fields.every((field, idx) => field.value === expected.fields[idx].value && _$n__$e_(actual.fieldTypes[idx], expected.fieldTypes[idx])));
|
|
1952
|
+
const check = (value, nodeId, depth) => {
|
|
1953
|
+
if (depth > 1024)
|
|
1954
|
+
return false;
|
|
1955
|
+
const node = graph.nodes[nodeId];
|
|
1956
|
+
if (node == null)
|
|
1957
|
+
return false;
|
|
1958
|
+
switch (node.kind) {
|
|
1959
|
+
case "dynamic": return true;
|
|
1960
|
+
case "nil": return value === null;
|
|
1961
|
+
case "unit": return value === undefined;
|
|
1962
|
+
case "bool": return typeof value === "boolean";
|
|
1963
|
+
case "number": return typeof value === "number";
|
|
1964
|
+
case "numeric": return typeof value === "number" && _$n_number_$o_fits_$q_(value, newTag(node.target));
|
|
1965
|
+
case "string": return typeof value === "string";
|
|
1966
|
+
case "symbol": return value instanceof CalcitSymbol;
|
|
1967
|
+
case "tag": return value instanceof CalcitTag;
|
|
1968
|
+
case "buffer": return value instanceof Uint8Array;
|
|
1969
|
+
case "cirru-quote": return value instanceof CalcitCirruQuote;
|
|
1970
|
+
case "optional": return value === null || check(value, node.inner, depth + 1);
|
|
1971
|
+
case "list": {
|
|
1972
|
+
if (!(value instanceof CalcitList || value instanceof CalcitSliceList))
|
|
1973
|
+
return false;
|
|
1974
|
+
for (const item of value.items())
|
|
1975
|
+
if (!check(item, node.inner, depth + 1))
|
|
1976
|
+
return false;
|
|
1977
|
+
return true;
|
|
1978
|
+
}
|
|
1979
|
+
case "set": return value instanceof CalcitSet && value.values().every(item => check(item, node.inner, depth + 1));
|
|
1980
|
+
case "map": return (value instanceof CalcitMap || value instanceof CalcitSliceMap) && value.pairs().every(([key, item]) => check(key, node.key, depth + 1) && check(item, node.value, depth + 1));
|
|
1981
|
+
case "ref": return value instanceof CalcitRef && check(value.value, node.inner, depth + 1);
|
|
1982
|
+
case "struct": return value instanceof CalcitStructValue && sameNominal(value.structRef, node.nominal) &&
|
|
1983
|
+
value.values.length === node.fields.length && node.fields.every(([name, child], idx) => value.fields[idx]?.value === name && check(value.values[idx], child, depth + 1));
|
|
1984
|
+
case "map-option":
|
|
1985
|
+
case "enum": {
|
|
1986
|
+
if (!(value instanceof CalcitEnumValue) || value.enumPrototype == null ||
|
|
1987
|
+
!sameNominal(value.enumPrototype.prototype.structRef, node.nominal.prototype.structRef) ||
|
|
1988
|
+
!_$n__$e_(value.enumPrototype.prototype, node.nominal.prototype) || !(value.tag instanceof CalcitTag))
|
|
1989
|
+
return false;
|
|
1990
|
+
if (node.kind === "map-option") {
|
|
1991
|
+
return value.tag.value === "none" ? value.extra.length === 0 : value.tag.value === "some" &&
|
|
1992
|
+
value.extra.length === 1 && check(value.extra[0], node.inner, depth + 1);
|
|
1993
|
+
}
|
|
1994
|
+
const tag = value.tag.value;
|
|
1995
|
+
const variant = node.variants.find(candidate => candidate.tag === tag);
|
|
1996
|
+
return variant != null && value.extra.length === variant.payload.length &&
|
|
1997
|
+
variant.payload.every((child, idx) => check(value.extra[idx], child, depth + 1));
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
};
|
|
2001
|
+
return check(input, graph.root, 0);
|
|
2002
|
+
}
|
|
1944
2003
|
const typed_edn_kind = (value) => {
|
|
1945
2004
|
if (value === null)
|
|
1946
2005
|
return "nil";
|
|
@@ -1980,75 +2039,75 @@ const typed_edn_error = (path, message) => {
|
|
|
1980
2039
|
const enum_prototype_name = (value) => {
|
|
1981
2040
|
return value instanceof CalcitEnumDef ? value.name() : value.name.value;
|
|
1982
2041
|
};
|
|
1983
|
-
const decode_typed_edn_node = (graph, nodeId, input, path, depth) => {
|
|
2042
|
+
const decode_typed_edn_node = (graph, nodeId, input, path, depth, fail = typed_edn_error) => {
|
|
1984
2043
|
if (depth > 1024)
|
|
1985
|
-
|
|
2044
|
+
fail(path, "decode nesting exceeds 1024");
|
|
1986
2045
|
const node = graph.nodes[nodeId];
|
|
1987
2046
|
if (node == null)
|
|
1988
|
-
|
|
2047
|
+
fail(path, `invalid data shape node #${nodeId}`);
|
|
1989
2048
|
switch (node.kind) {
|
|
1990
2049
|
case "nil":
|
|
1991
2050
|
if (input === null)
|
|
1992
2051
|
return null;
|
|
1993
|
-
return
|
|
2052
|
+
return fail(path, `expected nil, got ${typed_edn_kind(input)}`);
|
|
1994
2053
|
case "unit":
|
|
1995
2054
|
if (input === undefined)
|
|
1996
2055
|
return input;
|
|
1997
|
-
return
|
|
2056
|
+
return fail(path, `expected &unit, got ${typed_edn_kind(input)}`);
|
|
1998
2057
|
case "bool":
|
|
1999
2058
|
if (typeof input === "boolean")
|
|
2000
2059
|
return input;
|
|
2001
|
-
return
|
|
2060
|
+
return fail(path, `expected bool, got ${typed_edn_kind(input)}`);
|
|
2002
2061
|
case "number":
|
|
2003
2062
|
if (typeof input === "number")
|
|
2004
2063
|
return input;
|
|
2005
|
-
return
|
|
2064
|
+
return fail(path, `expected number, got ${typed_edn_kind(input)}`);
|
|
2006
2065
|
case "numeric":
|
|
2007
2066
|
if (typeof input !== "number")
|
|
2008
|
-
return
|
|
2067
|
+
return fail(path, `expected ${node.target}, got ${typed_edn_kind(input)}`);
|
|
2009
2068
|
if (_$n_number_$o_fits_$q_(input, newTag(node.target)))
|
|
2010
2069
|
return input;
|
|
2011
|
-
return
|
|
2070
|
+
return fail(path, `number ${input} does not fit ${node.target}`);
|
|
2012
2071
|
case "string":
|
|
2013
2072
|
if (typeof input === "string")
|
|
2014
2073
|
return input;
|
|
2015
|
-
return
|
|
2074
|
+
return fail(path, `expected string, got ${typed_edn_kind(input)}`);
|
|
2016
2075
|
case "symbol":
|
|
2017
2076
|
if (input instanceof CalcitSymbol)
|
|
2018
2077
|
return input;
|
|
2019
|
-
return
|
|
2078
|
+
return fail(path, `expected symbol, got ${typed_edn_kind(input)}`);
|
|
2020
2079
|
case "tag":
|
|
2021
2080
|
if (input instanceof CalcitTag)
|
|
2022
2081
|
return input;
|
|
2023
|
-
return
|
|
2082
|
+
return fail(path, `expected tag, got ${typed_edn_kind(input)}`);
|
|
2024
2083
|
case "buffer":
|
|
2025
2084
|
if (input instanceof Uint8Array)
|
|
2026
2085
|
return input;
|
|
2027
|
-
return
|
|
2086
|
+
return fail(path, `expected buffer, got ${typed_edn_kind(input)}`);
|
|
2028
2087
|
case "cirru-quote":
|
|
2029
2088
|
if (input instanceof CalcitCirruQuote)
|
|
2030
2089
|
return input;
|
|
2031
|
-
return
|
|
2090
|
+
return fail(path, `expected cirru-quote, got ${typed_edn_kind(input)}`);
|
|
2032
2091
|
case "optional":
|
|
2033
|
-
return input === null ? null : decode_typed_edn_node(graph, node.inner, input, path, depth + 1);
|
|
2092
|
+
return input === null ? null : decode_typed_edn_node(graph, node.inner, input, path, depth + 1, fail);
|
|
2034
2093
|
case "list": {
|
|
2035
2094
|
if (!(input instanceof CalcitList || input instanceof CalcitSliceList)) {
|
|
2036
|
-
return
|
|
2095
|
+
return fail(path, `expected list, got ${typed_edn_kind(input)}`);
|
|
2037
2096
|
}
|
|
2038
|
-
const values = Array.from(input.items()).map((value, idx) => decode_typed_edn_node(graph, node.inner, value, `${path}[${idx}]`, depth + 1));
|
|
2097
|
+
const values = Array.from(input.items()).map((value, idx) => decode_typed_edn_node(graph, node.inner, value, `${path}[${idx}]`, depth + 1, fail));
|
|
2039
2098
|
return new CalcitSliceList(values);
|
|
2040
2099
|
}
|
|
2041
2100
|
case "set": {
|
|
2042
2101
|
if (!(input instanceof CalcitSet || input instanceof TypedEdnSetView)) {
|
|
2043
|
-
return
|
|
2102
|
+
return fail(path, `expected set, got ${typed_edn_kind(input)}`);
|
|
2044
2103
|
}
|
|
2045
2104
|
const values = [];
|
|
2046
2105
|
const inputValues = input instanceof TypedEdnSetView ? input.items : input.values();
|
|
2047
2106
|
inputValues.forEach((value) => {
|
|
2048
2107
|
const itemPath = `${path}.item`;
|
|
2049
|
-
const decoded = decode_typed_edn_node(graph, node.inner, value, itemPath, depth + 1);
|
|
2108
|
+
const decoded = decode_typed_edn_node(graph, node.inner, value, itemPath, depth + 1, fail);
|
|
2050
2109
|
if (values.some((existing) => _$n__$e_(existing, decoded))) {
|
|
2051
|
-
|
|
2110
|
+
fail(itemPath, "duplicate decoded set value");
|
|
2052
2111
|
}
|
|
2053
2112
|
values.push(decoded);
|
|
2054
2113
|
});
|
|
@@ -2056,53 +2115,53 @@ const decode_typed_edn_node = (graph, nodeId, input, path, depth) => {
|
|
|
2056
2115
|
}
|
|
2057
2116
|
case "map": {
|
|
2058
2117
|
if (!(input instanceof CalcitMap || input instanceof CalcitSliceMap)) {
|
|
2059
|
-
return
|
|
2118
|
+
return fail(path, `expected map, got ${typed_edn_kind(input)}`);
|
|
2060
2119
|
}
|
|
2061
2120
|
const entries = [];
|
|
2062
2121
|
input.pairs().forEach(([key, value]) => {
|
|
2063
2122
|
const keyPath = `${path}.key`;
|
|
2064
|
-
const decodedKey = decode_typed_edn_node(graph, node.key, key, keyPath, depth + 1);
|
|
2123
|
+
const decodedKey = decode_typed_edn_node(graph, node.key, key, keyPath, depth + 1, fail);
|
|
2065
2124
|
for (let idx = 0; idx < entries.length; idx += 2) {
|
|
2066
2125
|
if (_$n__$e_(entries[idx], decodedKey)) {
|
|
2067
|
-
|
|
2126
|
+
fail(keyPath, "duplicate decoded map key");
|
|
2068
2127
|
}
|
|
2069
2128
|
}
|
|
2070
|
-
const decodedValue = decode_typed_edn_node(graph, node.value, value, `${path}.value`, depth + 1);
|
|
2129
|
+
const decodedValue = decode_typed_edn_node(graph, node.value, value, `${path}.value`, depth + 1, fail);
|
|
2071
2130
|
entries.push(decodedKey, decodedValue);
|
|
2072
2131
|
});
|
|
2073
2132
|
return new CalcitSliceMap(entries);
|
|
2074
2133
|
}
|
|
2075
2134
|
case "ref":
|
|
2076
2135
|
if (!(input instanceof CalcitRef))
|
|
2077
|
-
return
|
|
2078
|
-
return atom(decode_typed_edn_node(graph, node.inner, input.value, `${path}.value`, depth + 1));
|
|
2136
|
+
return fail(path, `expected atom, got ${typed_edn_kind(input)}`);
|
|
2137
|
+
return atom(decode_typed_edn_node(graph, node.inner, input.value, `${path}.value`, depth + 1, fail));
|
|
2079
2138
|
case "struct": {
|
|
2080
2139
|
if (!(input instanceof CalcitStructValue)) {
|
|
2081
|
-
return
|
|
2140
|
+
return fail(path, `expected struct :${node.nominal.name.value}, got ${typed_edn_kind(input)}`);
|
|
2082
2141
|
}
|
|
2083
2142
|
if (input.name.value !== node.nominal.name.value) {
|
|
2084
|
-
return
|
|
2143
|
+
return fail(path, `expected struct :${node.nominal.name.value}, got struct :${input.name.value}`);
|
|
2085
2144
|
}
|
|
2086
2145
|
const expectedNames = node.fields.map(([name]) => name);
|
|
2087
2146
|
const actualNames = input.fields.map((field) => field.value);
|
|
2088
2147
|
const missing = expectedNames.filter((name) => !actualNames.includes(name)).sort();
|
|
2089
2148
|
const unknown = actualNames.filter((name) => !expectedNames.includes(name)).sort();
|
|
2090
2149
|
if (missing.length > 0 || unknown.length > 0 || expectedNames.length !== actualNames.length) {
|
|
2091
|
-
return
|
|
2150
|
+
return fail(path, `struct :${node.nominal.name.value} fields mismatch; missing [${missing.join(", ")}], unknown [${unknown.join(", ")}]`);
|
|
2092
2151
|
}
|
|
2093
2152
|
const decodedFields = new Map();
|
|
2094
2153
|
node.fields.forEach(([name, fieldNode]) => {
|
|
2095
2154
|
const idx = actualNames.indexOf(name);
|
|
2096
|
-
decodedFields.set(name, decode_typed_edn_node(graph, fieldNode, input.values[idx], `${path}.${name}`, depth + 1));
|
|
2155
|
+
decodedFields.set(name, decode_typed_edn_node(graph, fieldNode, input.values[idx], `${path}.${name}`, depth + 1, fail));
|
|
2097
2156
|
});
|
|
2098
2157
|
// Re-align by name so values produced by older runtimes or external data
|
|
2099
2158
|
// still adopt the current nominal declaration's canonical field layout.
|
|
2100
2159
|
if (decodedFields.size !== node.nominal.fields.length) {
|
|
2101
|
-
return
|
|
2160
|
+
return fail(path, `struct :${node.nominal.name.value} decoder fields do not match its nominal declaration`);
|
|
2102
2161
|
}
|
|
2103
2162
|
const values = node.nominal.fields.map((field) => {
|
|
2104
2163
|
if (!decodedFields.has(field.value)) {
|
|
2105
|
-
return
|
|
2164
|
+
return fail(path, `struct :${node.nominal.name.value} is missing declared field :${field.value}`);
|
|
2106
2165
|
}
|
|
2107
2166
|
return decodedFields.get(field.value);
|
|
2108
2167
|
});
|
|
@@ -2110,28 +2169,28 @@ const decode_typed_edn_node = (graph, nodeId, input, path, depth) => {
|
|
|
2110
2169
|
}
|
|
2111
2170
|
case "enum": {
|
|
2112
2171
|
if (!(input instanceof CalcitEnumValue) || input.enumPrototype == null) {
|
|
2113
|
-
return
|
|
2172
|
+
return fail(path, `expected enum :${node.nominal.name()}, got ${typed_edn_kind(input)}`);
|
|
2114
2173
|
}
|
|
2115
2174
|
const actualEnumName = enum_prototype_name(input.enumPrototype);
|
|
2116
2175
|
if (actualEnumName !== node.nominal.name()) {
|
|
2117
|
-
return
|
|
2176
|
+
return fail(path, `expected enum :${node.nominal.name()}, got enum :${actualEnumName}`);
|
|
2118
2177
|
}
|
|
2119
2178
|
if (!(input.tag instanceof CalcitTag)) {
|
|
2120
|
-
return
|
|
2179
|
+
return fail(path, `enum :${node.nominal.name()} variant must be a tag`);
|
|
2121
2180
|
}
|
|
2122
2181
|
const inputTag = input.tag;
|
|
2123
2182
|
const variant = node.variants.find((candidate) => candidate.tag === inputTag.value);
|
|
2124
2183
|
if (variant == null) {
|
|
2125
|
-
return
|
|
2184
|
+
return fail(path, `enum :${node.nominal.name()} has no variant :${inputTag.value}`);
|
|
2126
2185
|
}
|
|
2127
2186
|
if (variant.payload.length !== input.extra.length) {
|
|
2128
|
-
return
|
|
2187
|
+
return fail(path, `enum :${node.nominal.name()} variant :${variant.tag} expects ${variant.payload.length} payload(s), got ${input.extra.length}`);
|
|
2129
2188
|
}
|
|
2130
|
-
const values = variant.payload.map((payloadNode, idx) => decode_typed_edn_node(graph, payloadNode, input.extra[idx], `${path}.payload[${idx}]`, depth + 1));
|
|
2189
|
+
const values = variant.payload.map((payloadNode, idx) => decode_typed_edn_node(graph, payloadNode, input.extra[idx], `${path}.payload[${idx}]`, depth + 1, fail));
|
|
2131
2190
|
return new CalcitEnumValue(newTag(variant.tag), values, node.nominal);
|
|
2132
2191
|
}
|
|
2133
2192
|
default:
|
|
2134
|
-
return
|
|
2193
|
+
return fail(path, `invalid data shape node kind: ${node.kind}`);
|
|
2135
2194
|
}
|
|
2136
2195
|
};
|
|
2137
2196
|
export let parse_cirru_edn_as = (code, graph) => {
|
|
@@ -2282,7 +2341,7 @@ const decode_runtime_map_node = (graph, nodeId, input, path, depth) => {
|
|
|
2282
2341
|
return new CalcitEnumValue(newTag(variant.tag), values, node.nominal);
|
|
2283
2342
|
}
|
|
2284
2343
|
default:
|
|
2285
|
-
return decode_typed_edn_node(graph, nodeId, input, path, depth);
|
|
2344
|
+
return decode_typed_edn_node(graph, nodeId, input, path, depth, map_decode_error);
|
|
2286
2345
|
}
|
|
2287
2346
|
};
|
|
2288
2347
|
export let decode_map_as = (value, graph) => {
|
|
@@ -2808,6 +2867,19 @@ export function _$n_trait_call(traitDef, method, obj, ...args) {
|
|
|
2808
2867
|
}
|
|
2809
2868
|
throw new Error(`&trait-call: cannot find impl for trait ${traitDef.name.toString()} on ${toString(obj, true)}. Hint: use defimpl to create impls tagged by trait.`);
|
|
2810
2869
|
}
|
|
2870
|
+
export let _$n_map_$o_fold_kv = (m, initial, reducer) => {
|
|
2871
|
+
if (!(m instanceof CalcitMap || m instanceof CalcitSliceMap)) {
|
|
2872
|
+
throw new Error("&map:fold-kv expected a Map");
|
|
2873
|
+
}
|
|
2874
|
+
if (typeof reducer !== "function") {
|
|
2875
|
+
throw new Error("&map:fold-kv expected a callback function");
|
|
2876
|
+
}
|
|
2877
|
+
let accumulator = initial;
|
|
2878
|
+
for (const [key, value] of m.pairs()) {
|
|
2879
|
+
accumulator = reducer(accumulator, key, value);
|
|
2880
|
+
}
|
|
2881
|
+
return accumulator;
|
|
2882
|
+
};
|
|
2811
2883
|
export let _$n_map_$o_to_list = (m) => {
|
|
2812
2884
|
if (m instanceof CalcitMap || m instanceof CalcitSliceMap) {
|
|
2813
2885
|
let ys = [];
|
|
@@ -2894,23 +2966,30 @@ export let _$n_map_$o_diff_triple = (a, b) => {
|
|
|
2894
2966
|
throw new Error("&map:diff-triple expected 2 maps");
|
|
2895
2967
|
}
|
|
2896
2968
|
};
|
|
2969
|
+
// Bitwise operands are integers within i32 (docs/data/number.md); JS would otherwise wrap or truncate them.
|
|
2970
|
+
let bit_operand = (name, x) => {
|
|
2971
|
+
if (typeof x !== "number" || !Number.isInteger(x) || x < -2147483648 || x > 2147483647) {
|
|
2972
|
+
throw new Error(`&math:${name} operand expected an integer within i32, but got: ${x}`);
|
|
2973
|
+
}
|
|
2974
|
+
return x;
|
|
2975
|
+
};
|
|
2897
2976
|
export let bit_shr = (base, step) => {
|
|
2898
|
-
return base >> step;
|
|
2977
|
+
return bit_operand("bit-shr", base) >> bit_operand("bit-shr", step);
|
|
2899
2978
|
};
|
|
2900
2979
|
export let bit_shl = (base, step) => {
|
|
2901
|
-
return base << step;
|
|
2980
|
+
return bit_operand("bit-shl", base) << bit_operand("bit-shl", step);
|
|
2902
2981
|
};
|
|
2903
2982
|
export let bit_and = (a, b) => {
|
|
2904
|
-
return a & b;
|
|
2983
|
+
return bit_operand("bit-and", a) & bit_operand("bit-and", b);
|
|
2905
2984
|
};
|
|
2906
2985
|
export let bit_or = (a, b) => {
|
|
2907
|
-
return a | b;
|
|
2986
|
+
return bit_operand("bit-or", a) | bit_operand("bit-or", b);
|
|
2908
2987
|
};
|
|
2909
2988
|
export let bit_xor = (a, b) => {
|
|
2910
|
-
return a ^ b;
|
|
2989
|
+
return bit_operand("bit-xor", a) ^ bit_operand("bit-xor", b);
|
|
2911
2990
|
};
|
|
2912
2991
|
export let bit_not = (a) => {
|
|
2913
|
-
return ~a;
|
|
2992
|
+
return ~bit_operand("bit-not", a);
|
|
2914
2993
|
};
|
|
2915
2994
|
export let _$n_list_$o_to_set = (xs) => {
|
|
2916
2995
|
var result = [];
|
|
@@ -2933,11 +3012,41 @@ export let _$n_list_$o_distinct = (xs) => {
|
|
|
2933
3012
|
}
|
|
2934
3013
|
return new CalcitSliceList(result);
|
|
2935
3014
|
};
|
|
3015
|
+
// Longest padded string, the V8 string length limit, so every backend fails at the same size.
|
|
3016
|
+
const MAX_PADDED_LEN = 536870888;
|
|
3017
|
+
// Padding for `&str:pad-left` / `&str:pad-right`, measured in Unicode scalars like `&str:count`
|
|
3018
|
+
// (not UTF-16 units, so astral characters are never split). NaN and negative lengths pad nothing,
|
|
3019
|
+
// and so does an empty pattern.
|
|
3020
|
+
const strPadding = (procName, s, size, pattern) => {
|
|
3021
|
+
if (size > MAX_PADDED_LEN) {
|
|
3022
|
+
throw new Error(`${procName} expected a finite length up to ${MAX_PADDED_LEN}, but received: ${format_calcit_number(size)}`);
|
|
3023
|
+
}
|
|
3024
|
+
if (!(size > 0) || pattern === "")
|
|
3025
|
+
return "";
|
|
3026
|
+
let missing = Math.floor(size);
|
|
3027
|
+
for (const _scalar of s) {
|
|
3028
|
+
missing -= 1;
|
|
3029
|
+
if (missing <= 0)
|
|
3030
|
+
return "";
|
|
3031
|
+
}
|
|
3032
|
+
let patternCount = 0;
|
|
3033
|
+
for (const _scalar of pattern)
|
|
3034
|
+
patternCount += 1;
|
|
3035
|
+
let padding = pattern.repeat(Math.floor(missing / patternCount));
|
|
3036
|
+
let rest = missing % patternCount;
|
|
3037
|
+
for (const scalar of pattern) {
|
|
3038
|
+
if (rest <= 0)
|
|
3039
|
+
break;
|
|
3040
|
+
padding += scalar;
|
|
3041
|
+
rest -= 1;
|
|
3042
|
+
}
|
|
3043
|
+
return padding;
|
|
3044
|
+
};
|
|
2936
3045
|
export let _$n_str_$o_pad_left = (s, size, pattern) => {
|
|
2937
|
-
return s
|
|
3046
|
+
return strPadding("&str:pad-left", s, size, pattern) + s;
|
|
2938
3047
|
};
|
|
2939
3048
|
export let _$n_str_$o_pad_right = (s, size, pattern) => {
|
|
2940
|
-
return s
|
|
3049
|
+
return s + strPadding("&str:pad-right", s, size, pattern);
|
|
2941
3050
|
};
|
|
2942
3051
|
export let _$n_get_os = () => {
|
|
2943
3052
|
return newTag("js-engine");
|
|
@@ -2993,8 +3102,12 @@ export let macroexpand_all = unavailableProc;
|
|
|
2993
3102
|
export let _$n_get_calcit_running_mode = unavailableProc;
|
|
2994
3103
|
export let _$n_get_def_doc = unavailableProc;
|
|
2995
3104
|
export let _$n_get_def_schema = unavailableProc;
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
3105
|
+
// A missing native lifecycle capability must not look like a successful Unit.
|
|
3106
|
+
const unavailableNativeFfi = (operation) => {
|
|
3107
|
+
throw new Error(`${operation}: native FFI capability is not available for calcit-js`);
|
|
3108
|
+
};
|
|
3109
|
+
export let _$n_ffi_response_resolve = (..._args) => unavailableNativeFfi("&ffi-response-resolve");
|
|
3110
|
+
export let _$n_ffi_response_reject = (..._args) => unavailableNativeFfi("&ffi-response-reject");
|
|
3111
|
+
export let _$n_ffi_task_cancel = (..._args) => unavailableNativeFfi("&ffi-task-cancel");
|
|
2999
3112
|
// already handled in code emitter
|
|
3000
3113
|
export let raise = unavailableProc;
|
package/lib/js-cirru.d.mts
CHANGED
|
@@ -12,7 +12,7 @@ export declare class CalcitCirruQuote {
|
|
|
12
12
|
/** provide a simple text representation in Console or std out, with indentations */
|
|
13
13
|
textForm(): string;
|
|
14
14
|
}
|
|
15
|
-
export declare let format_cirru: (data: CalcitCirruQuote | CalcitList, useInline
|
|
15
|
+
export declare let format_cirru: (data: CalcitCirruQuote | CalcitList, useInline?: boolean) => string;
|
|
16
16
|
export declare let format_cirru_one_liner: (data: CalcitCirruQuote | CalcitList) => string;
|
|
17
17
|
/** better use string version of Cirru EDN in future */
|
|
18
18
|
export declare let to_cirru_edn: (x: CalcitValue) => CirruEdnFormat;
|
package/lib/js-cirru.mjs
CHANGED
|
@@ -160,7 +160,12 @@ export class CalcitCirruQuote {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
-
export let format_cirru = (data, useInline)
|
|
163
|
+
export let format_cirru = function (data, useInline) {
|
|
164
|
+
if (arguments.length < 2)
|
|
165
|
+
useInline = false;
|
|
166
|
+
if (typeof useInline !== "boolean") {
|
|
167
|
+
throw new Error("format-cirru requires a boolean inline option");
|
|
168
|
+
}
|
|
164
169
|
if (data instanceof CalcitCirruQuote) {
|
|
165
170
|
return writeCirruCode(data.value, { useInline });
|
|
166
171
|
}
|
|
@@ -563,7 +568,9 @@ export let extract_cirru_edn = (x, options) => {
|
|
|
563
568
|
export let extract_cirru_edn_for_typed = (x, options) => {
|
|
564
569
|
return extract_cirru_edn_inner(x, options, true);
|
|
565
570
|
};
|
|
566
|
-
export let format_cirru_edn = (data, useInline
|
|
571
|
+
export let format_cirru_edn = function (data, useInline) {
|
|
572
|
+
if (arguments.length < 2)
|
|
573
|
+
useInline = true;
|
|
567
574
|
if (typeof useInline !== "boolean") {
|
|
568
575
|
throw new Error("format-cirru-edn requires a boolean inline option");
|
|
569
576
|
}
|
package/lib/js-primes.d.mts
CHANGED
|
@@ -12,4 +12,6 @@ import { CalcitCirruQuote } from "./js-cirru.mjs";
|
|
|
12
12
|
import { CalcitTrait } from "./js-trait.mjs";
|
|
13
13
|
export type CalcitValue = string | number | boolean | CalcitMap | CalcitSliceMap | CalcitList | CalcitSliceList | CalcitSet | CalcitTag | CalcitSymbol | CalcitRef | CalcitEnumValue | CalcitFn | CalcitRecur | CalcitStructValue | CalcitImpl | CalcitTrait | CalcitStructDef | CalcitEnumDef | CalcitCirruQuote | Uint8Array | undefined | null;
|
|
14
14
|
export declare let isLiteral: (x: CalcitValue) => boolean;
|
|
15
|
+
/** total order over numbers: `-0` equals `0`, NaN equals NaN and sorts after every number */
|
|
16
|
+
export declare let compareNumbers: (x: number, y: number) => number;
|
|
15
17
|
export declare let _$n_compare: (a: CalcitValue, b: CalcitValue) => number;
|