@calcit/procs 0.12.59 → 0.13.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/.yarn/install-state.gz +0 -0
- package/RFCs/08-04-strict-cirru-edn-decoding-rfc.md +342 -0
- package/RFCs/08-05-systematic-nil-reduction-rfc.md +174 -0
- package/RFCs/README.md +3 -1
- package/editing-history/202608050112-nil-reduction-foundation.md +33 -0
- package/editing-history/202608050943-pr-review-and-test-nil-reduction.md +29 -0
- package/editing-history/202608051010-explicit-unit-without-nil.md +30 -0
- package/editing-history/202608051720-nominal-option-api-migration.md +30 -0
- package/editing-history/202608051924-expand-option-collection-apis.md +31 -0
- package/editing-history/202608052014-js-nullish-boundary.md +29 -0
- package/editing-history/202608052255-nominal-reflection-and-destruct-apis.md +29 -0
- package/editing-history/202608060104-public-nil-contract-freeze.md +36 -0
- package/editing-history/202608060215-fix-hashmap-option-docs.md +4 -0
- package/editing-history/202608060220-release-0.13.0.md +5 -0
- package/history/202608040125-js-runtime-impl-identity.md +6 -0
- package/history/202608040241-strict-cirru-edn-decoding.md +27 -0
- package/history/202608040954-strict-edn-review-followups.md +16 -0
- package/history/202608041206-harden-js-impl-brand-check.md +5 -0
- package/history/202608041223-cover-inherited-js-impl-brand.md +4 -0
- package/history/202608041911-typed-data-shape-patch.md +30 -0
- package/history/202608042252-reject-decoded-edn-collisions.md +11 -0
- package/history/202608042338-strict-edn-js-collision-parity.md +21 -0
- package/history/202608042344-isolate-strict-edn-dependency-test.md +13 -0
- package/lib/calcit.procs.d.mts +35 -4
- package/lib/calcit.procs.mjs +233 -11
- package/lib/js-cirru.d.mts +2 -0
- package/lib/js-cirru.mjs +39 -17
- package/lib/js-impl.d.mts +1 -0
- package/lib/js-impl.mjs +28 -0
- package/lib/package.json +3 -2
- package/lib/typed-edn.d.mts +4 -0
- package/lib/typed-edn.mjs +5 -0
- package/package.json +3 -2
- package/ts-src/calcit.procs.mts +241 -13
- package/ts-src/js-cirru.mts +39 -19
- package/ts-src/js-impl.mts +33 -0
- package/ts-src/typed-edn.mts +7 -0
package/ts-src/calcit.procs.mts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
hashFunction,
|
|
22
22
|
} from "./calcit-data.mjs";
|
|
23
23
|
|
|
24
|
-
import { CalcitRef } from "./js-ref.mjs";
|
|
24
|
+
import { CalcitRef, atom } from "./js-ref.mjs";
|
|
25
25
|
import { CalcitRecord } from "./js-record.mjs";
|
|
26
26
|
import { CalcitImpl } from "./js-impl.mjs";
|
|
27
27
|
import { CalcitStruct } from "./js-struct.mjs";
|
|
@@ -50,7 +50,8 @@ import { CalcitList, CalcitSliceList, foldl } from "./js-list.mjs";
|
|
|
50
50
|
import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
|
|
51
51
|
import { CalcitSet } from "./js-set.mjs";
|
|
52
52
|
import { CalcitTuple } from "./js-tuple.mjs";
|
|
53
|
-
import { to_calcit_data, extract_cirru_edn, CalcitCirruQuote } from "./js-cirru.mjs";
|
|
53
|
+
import { to_calcit_data, extract_cirru_edn, extract_cirru_edn_for_typed, CalcitCirruQuote } from "./js-cirru.mjs";
|
|
54
|
+
import { TypedEdnSetView } from "./typed-edn.mjs";
|
|
54
55
|
|
|
55
56
|
let inNodeJs = typeof process !== "undefined" && process?.release?.name === "node";
|
|
56
57
|
|
|
@@ -299,7 +300,9 @@ export let _$n_impl_$o__$o_new = (name: CalcitValue, ...pairs: CalcitValue[]): C
|
|
|
299
300
|
let sourcePairs = pairs;
|
|
300
301
|
if (pairs.length === 1 && pairs[0] instanceof CalcitImpl) {
|
|
301
302
|
const sourceImpl = pairs[0];
|
|
302
|
-
sourcePairs = sourceImpl.fields.map(
|
|
303
|
+
sourcePairs = sourceImpl.fields.map(
|
|
304
|
+
(field, idx) => new CalcitTuple(newTag(field.value), [sourceImpl.values[idx]], null)
|
|
305
|
+
);
|
|
303
306
|
}
|
|
304
307
|
for (let idx = 0; idx < sourcePairs.length; idx++) {
|
|
305
308
|
const pairValue = sourcePairs[idx];
|
|
@@ -939,6 +942,18 @@ export let _$n_list_$o_first = (xs: CalcitValue): CalcitValue => {
|
|
|
939
942
|
console.error(xs);
|
|
940
943
|
throw new Error("Expected a list");
|
|
941
944
|
};
|
|
945
|
+
|
|
946
|
+
export let _$n_list_$o_last = (xs: CalcitValue): CalcitValue => {
|
|
947
|
+
if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
|
|
948
|
+
if (xs.isEmpty()) {
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
return xs.get(xs.len() - 1);
|
|
952
|
+
}
|
|
953
|
+
console.error(xs);
|
|
954
|
+
throw new Error("Expected a list");
|
|
955
|
+
};
|
|
956
|
+
|
|
942
957
|
export let _$n_str_$o_first = (xs: CalcitValue): CalcitValue => {
|
|
943
958
|
if (typeof xs === "string") {
|
|
944
959
|
return xs[0];
|
|
@@ -980,10 +995,10 @@ export let timeout_call = (duration: number, f: CalcitFn): null => {
|
|
|
980
995
|
return null;
|
|
981
996
|
};
|
|
982
997
|
|
|
983
|
-
export let _$n_list_$o_rest = (xs: CalcitValue):
|
|
998
|
+
export let _$n_list_$o_rest = (xs: CalcitValue): CalcitList | CalcitSliceList => {
|
|
984
999
|
if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
|
|
985
1000
|
if (xs.len() === 0) {
|
|
986
|
-
return
|
|
1001
|
+
return xs.slice(0, 0);
|
|
987
1002
|
}
|
|
988
1003
|
return xs.rest();
|
|
989
1004
|
}
|
|
@@ -1038,10 +1053,10 @@ export let last = (xs: CalcitValue): CalcitValue => {
|
|
|
1038
1053
|
throw new Error("Data not ready for last");
|
|
1039
1054
|
};
|
|
1040
1055
|
|
|
1041
|
-
export let butlast = (xs: CalcitValue):
|
|
1056
|
+
export let butlast = (xs: CalcitValue): CalcitList | CalcitSliceList | string => {
|
|
1042
1057
|
if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
|
|
1043
1058
|
if (xs.len() === 0) {
|
|
1044
|
-
return
|
|
1059
|
+
return xs.slice(0, 0);
|
|
1045
1060
|
}
|
|
1046
1061
|
return xs.slice(0, xs.len() - 1);
|
|
1047
1062
|
}
|
|
@@ -1346,12 +1361,12 @@ export let _$n_str_$o_find_index = (x: string, y: string): number => {
|
|
|
1346
1361
|
return x.indexOf(y);
|
|
1347
1362
|
};
|
|
1348
1363
|
|
|
1349
|
-
export let
|
|
1350
|
-
|
|
1351
|
-
if (Number.isNaN(value)) {
|
|
1364
|
+
export let _$n_parse_float = (x: string): number | null => {
|
|
1365
|
+
if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(x)) {
|
|
1352
1366
|
return null;
|
|
1353
1367
|
}
|
|
1354
|
-
|
|
1368
|
+
const value = Number(x);
|
|
1369
|
+
return Number.isNaN(value) ? null : value;
|
|
1355
1370
|
};
|
|
1356
1371
|
export let trim = (x: string, c: string): string => {
|
|
1357
1372
|
if (c != null) {
|
|
@@ -1422,7 +1437,7 @@ export let js_delete = (obj: any, name: string): any => {
|
|
|
1422
1437
|
return delete obj[name];
|
|
1423
1438
|
};
|
|
1424
1439
|
|
|
1425
|
-
export let
|
|
1440
|
+
export let _$n_get_env = (name: string, v0?: CalcitValue): CalcitValue => {
|
|
1426
1441
|
let v = undefined;
|
|
1427
1442
|
if (inNodeJs) {
|
|
1428
1443
|
// only available for Node.js
|
|
@@ -1436,9 +1451,14 @@ export let get_env = (name: string, v0: string): string => {
|
|
|
1436
1451
|
if (v == null && v0 == null) {
|
|
1437
1452
|
console.warn(`(get-env "${name}"): config not found`);
|
|
1438
1453
|
}
|
|
1439
|
-
return v ?? v0;
|
|
1454
|
+
return v ?? v0 ?? null;
|
|
1440
1455
|
};
|
|
1441
1456
|
|
|
1457
|
+
// Keep already-emitted JS bundles loadable across the nominal Option migration.
|
|
1458
|
+
// Newly generated code calls the internal raw proc; source-level `get-env`
|
|
1459
|
+
// remains the typed Option wrapper defined in calcit.core.
|
|
1460
|
+
export let get_env = _$n_get_env;
|
|
1461
|
+
|
|
1442
1462
|
export let turn_tag = (x: CalcitValue): CalcitTag => {
|
|
1443
1463
|
if (typeof x === "string") {
|
|
1444
1464
|
return newTag(x);
|
|
@@ -1656,6 +1676,214 @@ export let parse_cirru_edn = (code: string, options: CalcitValue) => {
|
|
|
1656
1676
|
}
|
|
1657
1677
|
};
|
|
1658
1678
|
|
|
1679
|
+
type DataShapeNode =
|
|
1680
|
+
| { kind: "unit" | "bool" | "number" | "string" | "symbol" | "tag" | "buffer" | "cirru-quote" }
|
|
1681
|
+
| { kind: "optional" | "list" | "set" | "ref"; inner: number }
|
|
1682
|
+
| { kind: "map"; key: number; value: number }
|
|
1683
|
+
| { kind: "struct"; nominal: CalcitStruct; fields: Array<[string, number]> }
|
|
1684
|
+
| { kind: "enum"; nominal: CalcitEnum; variants: Array<{ tag: string; payload: number[] }> };
|
|
1685
|
+
|
|
1686
|
+
type DataShapeGraph = {
|
|
1687
|
+
version: number;
|
|
1688
|
+
root: number;
|
|
1689
|
+
fingerprint: string;
|
|
1690
|
+
nodes: DataShapeNode[];
|
|
1691
|
+
};
|
|
1692
|
+
|
|
1693
|
+
const typed_edn_kind = (value: any): string => {
|
|
1694
|
+
if (value == null) return "nil";
|
|
1695
|
+
if (typeof value === "boolean") return "bool";
|
|
1696
|
+
if (typeof value === "number") return "number";
|
|
1697
|
+
if (typeof value === "string") return "string";
|
|
1698
|
+
if (value instanceof CalcitSymbol) return "symbol";
|
|
1699
|
+
if (value instanceof CalcitTag) return "tag";
|
|
1700
|
+
if (value instanceof CalcitList || value instanceof CalcitSliceList) return "list";
|
|
1701
|
+
if (value instanceof CalcitMap || value instanceof CalcitSliceMap) return "map";
|
|
1702
|
+
if (value instanceof CalcitSet || value instanceof TypedEdnSetView) return "set";
|
|
1703
|
+
if (value instanceof CalcitRecord) return "record";
|
|
1704
|
+
if (value instanceof CalcitTuple) return value.enumPrototype == null ? "tuple" : "enum";
|
|
1705
|
+
if (value instanceof CalcitRef) return "atom";
|
|
1706
|
+
if (value instanceof CalcitCirruQuote) return "cirru-quote";
|
|
1707
|
+
if (value instanceof Uint8Array) return "buffer";
|
|
1708
|
+
return "unsupported";
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1711
|
+
const typed_edn_error = (path: string, message: string): never => {
|
|
1712
|
+
throw new Error(`parse-cirru-edn-as failed at ${path}: ${message}`);
|
|
1713
|
+
};
|
|
1714
|
+
|
|
1715
|
+
const enum_prototype_name = (value: CalcitEnum | CalcitRecord): string => {
|
|
1716
|
+
return value instanceof CalcitEnum ? value.name() : value.name.value;
|
|
1717
|
+
};
|
|
1718
|
+
|
|
1719
|
+
const decode_typed_edn_node = (graph: DataShapeGraph, nodeId: number, input: any, path: string, depth: number): any => {
|
|
1720
|
+
if (depth > 1024) typed_edn_error(path, "decode nesting exceeds 1024");
|
|
1721
|
+
const node = graph.nodes[nodeId];
|
|
1722
|
+
if (node == null) typed_edn_error(path, `invalid data shape node #${nodeId}`);
|
|
1723
|
+
|
|
1724
|
+
switch (node.kind) {
|
|
1725
|
+
case "unit":
|
|
1726
|
+
if (input == null) return null;
|
|
1727
|
+
return typed_edn_error(path, `expected nil, got ${typed_edn_kind(input)}`);
|
|
1728
|
+
case "bool":
|
|
1729
|
+
if (typeof input === "boolean") return input;
|
|
1730
|
+
return typed_edn_error(path, `expected bool, got ${typed_edn_kind(input)}`);
|
|
1731
|
+
case "number":
|
|
1732
|
+
if (typeof input === "number") return input;
|
|
1733
|
+
return typed_edn_error(path, `expected number, got ${typed_edn_kind(input)}`);
|
|
1734
|
+
case "string":
|
|
1735
|
+
if (typeof input === "string") return input;
|
|
1736
|
+
return typed_edn_error(path, `expected string, got ${typed_edn_kind(input)}`);
|
|
1737
|
+
case "symbol":
|
|
1738
|
+
if (input instanceof CalcitSymbol) return input;
|
|
1739
|
+
return typed_edn_error(path, `expected symbol, got ${typed_edn_kind(input)}`);
|
|
1740
|
+
case "tag":
|
|
1741
|
+
if (input instanceof CalcitTag) return input;
|
|
1742
|
+
return typed_edn_error(path, `expected tag, got ${typed_edn_kind(input)}`);
|
|
1743
|
+
case "buffer":
|
|
1744
|
+
if (input instanceof Uint8Array) return input;
|
|
1745
|
+
return typed_edn_error(path, `expected buffer, got ${typed_edn_kind(input)}`);
|
|
1746
|
+
case "cirru-quote":
|
|
1747
|
+
if (input instanceof CalcitCirruQuote) return input;
|
|
1748
|
+
return typed_edn_error(path, `expected cirru-quote, got ${typed_edn_kind(input)}`);
|
|
1749
|
+
case "optional":
|
|
1750
|
+
return input == null ? null : decode_typed_edn_node(graph, node.inner, input, path, depth + 1);
|
|
1751
|
+
case "list": {
|
|
1752
|
+
if (!(input instanceof CalcitList || input instanceof CalcitSliceList)) {
|
|
1753
|
+
return typed_edn_error(path, `expected list, got ${typed_edn_kind(input)}`);
|
|
1754
|
+
}
|
|
1755
|
+
const values = Array.from(input.items()).map((value, idx) =>
|
|
1756
|
+
decode_typed_edn_node(graph, node.inner, value, `${path}[${idx}]`, depth + 1)
|
|
1757
|
+
);
|
|
1758
|
+
return new CalcitSliceList(values);
|
|
1759
|
+
}
|
|
1760
|
+
case "set": {
|
|
1761
|
+
if (!(input instanceof CalcitSet || input instanceof TypedEdnSetView)) {
|
|
1762
|
+
return typed_edn_error(path, `expected set, got ${typed_edn_kind(input)}`);
|
|
1763
|
+
}
|
|
1764
|
+
const values: any[] = [];
|
|
1765
|
+
const inputValues = input instanceof TypedEdnSetView ? input.items : input.values();
|
|
1766
|
+
inputValues.forEach((value) => {
|
|
1767
|
+
const itemPath = `${path}.item`;
|
|
1768
|
+
const decoded = decode_typed_edn_node(graph, node.inner, value, itemPath, depth + 1);
|
|
1769
|
+
if (values.some((existing) => _$n__$e_(existing, decoded))) {
|
|
1770
|
+
typed_edn_error(itemPath, "duplicate decoded set value");
|
|
1771
|
+
}
|
|
1772
|
+
values.push(decoded);
|
|
1773
|
+
});
|
|
1774
|
+
return new CalcitSet(values);
|
|
1775
|
+
}
|
|
1776
|
+
case "map": {
|
|
1777
|
+
if (!(input instanceof CalcitMap || input instanceof CalcitSliceMap)) {
|
|
1778
|
+
return typed_edn_error(path, `expected map, got ${typed_edn_kind(input)}`);
|
|
1779
|
+
}
|
|
1780
|
+
const entries: any[] = [];
|
|
1781
|
+
input.pairs().forEach(([key, value]) => {
|
|
1782
|
+
const keyPath = `${path}.key`;
|
|
1783
|
+
const decodedKey = decode_typed_edn_node(graph, node.key, key, keyPath, depth + 1);
|
|
1784
|
+
for (let idx = 0; idx < entries.length; idx += 2) {
|
|
1785
|
+
if (_$n__$e_(entries[idx], decodedKey)) {
|
|
1786
|
+
typed_edn_error(keyPath, "duplicate decoded map key");
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
const decodedValue = decode_typed_edn_node(graph, node.value, value, `${path}.value`, depth + 1);
|
|
1790
|
+
entries.push(decodedKey, decodedValue);
|
|
1791
|
+
});
|
|
1792
|
+
return new CalcitSliceMap(entries);
|
|
1793
|
+
}
|
|
1794
|
+
case "ref":
|
|
1795
|
+
if (!(input instanceof CalcitRef)) return typed_edn_error(path, `expected atom, got ${typed_edn_kind(input)}`);
|
|
1796
|
+
return atom(decode_typed_edn_node(graph, node.inner, input.value, `${path}.value`, depth + 1));
|
|
1797
|
+
case "struct": {
|
|
1798
|
+
if (!(input instanceof CalcitRecord)) {
|
|
1799
|
+
return typed_edn_error(path, `expected record :${node.nominal.name.value}, got ${typed_edn_kind(input)}`);
|
|
1800
|
+
}
|
|
1801
|
+
if (input.name.value !== node.nominal.name.value) {
|
|
1802
|
+
return typed_edn_error(path, `expected record :${node.nominal.name.value}, got record :${input.name.value}`);
|
|
1803
|
+
}
|
|
1804
|
+
const expectedNames = node.fields.map(([name]) => name);
|
|
1805
|
+
const actualNames = input.fields.map((field) => field.value);
|
|
1806
|
+
const missing = expectedNames.filter((name) => !actualNames.includes(name)).sort();
|
|
1807
|
+
const unknown = actualNames.filter((name) => !expectedNames.includes(name)).sort();
|
|
1808
|
+
if (missing.length > 0 || unknown.length > 0 || expectedNames.length !== actualNames.length) {
|
|
1809
|
+
return typed_edn_error(
|
|
1810
|
+
path,
|
|
1811
|
+
`record :${node.nominal.name.value} fields mismatch; missing [${missing.join(", ")}], unknown [${unknown.join(", ")}]`
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
const decodedFields = new Map<string, CalcitValue>();
|
|
1815
|
+
node.fields.forEach(([name, fieldNode]) => {
|
|
1816
|
+
const idx = actualNames.indexOf(name);
|
|
1817
|
+
decodedFields.set(name, decode_typed_edn_node(graph, fieldNode, input.values[idx], `${path}.${name}`, depth + 1));
|
|
1818
|
+
});
|
|
1819
|
+
// Native declarations use lexical field order while the JS runtime keeps
|
|
1820
|
+
// its interned-tag order. Re-align values to the nominal JS declaration.
|
|
1821
|
+
if (decodedFields.size !== node.nominal.fields.length) {
|
|
1822
|
+
return typed_edn_error(path, `record :${node.nominal.name.value} decoder fields do not match its nominal declaration`);
|
|
1823
|
+
}
|
|
1824
|
+
const values = node.nominal.fields.map((field) => {
|
|
1825
|
+
if (!decodedFields.has(field.value)) {
|
|
1826
|
+
return typed_edn_error(path, `record :${node.nominal.name.value} is missing declared field :${field.value}`);
|
|
1827
|
+
}
|
|
1828
|
+
return decodedFields.get(field.value)!;
|
|
1829
|
+
});
|
|
1830
|
+
return new CalcitRecord(node.nominal.name, node.nominal.fields, values, node.nominal);
|
|
1831
|
+
}
|
|
1832
|
+
case "enum": {
|
|
1833
|
+
if (!(input instanceof CalcitTuple) || input.enumPrototype == null) {
|
|
1834
|
+
return typed_edn_error(path, `expected enum :${node.nominal.name()}, got ${typed_edn_kind(input)}`);
|
|
1835
|
+
}
|
|
1836
|
+
const actualEnumName = enum_prototype_name(input.enumPrototype);
|
|
1837
|
+
if (actualEnumName !== node.nominal.name()) {
|
|
1838
|
+
return typed_edn_error(path, `expected enum :${node.nominal.name()}, got enum :${actualEnumName}`);
|
|
1839
|
+
}
|
|
1840
|
+
if (!(input.tag instanceof CalcitTag)) {
|
|
1841
|
+
return typed_edn_error(path, `enum :${node.nominal.name()} variant must be a tag`);
|
|
1842
|
+
}
|
|
1843
|
+
const inputTag = input.tag;
|
|
1844
|
+
const variant = node.variants.find((candidate) => candidate.tag === inputTag.value);
|
|
1845
|
+
if (variant == null) {
|
|
1846
|
+
return typed_edn_error(path, `enum :${node.nominal.name()} has no variant :${inputTag.value}`);
|
|
1847
|
+
}
|
|
1848
|
+
if (variant.payload.length !== input.extra.length) {
|
|
1849
|
+
return typed_edn_error(
|
|
1850
|
+
path,
|
|
1851
|
+
`enum :${node.nominal.name()} variant :${variant.tag} expects ${variant.payload.length} payload(s), got ${input.extra.length}`
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
const values = variant.payload.map((payloadNode, idx) =>
|
|
1855
|
+
decode_typed_edn_node(graph, payloadNode, input.extra[idx], `${path}.payload[${idx}]`, depth + 1)
|
|
1856
|
+
);
|
|
1857
|
+
return new CalcitTuple(newTag(variant.tag), values, node.nominal);
|
|
1858
|
+
}
|
|
1859
|
+
default:
|
|
1860
|
+
return typed_edn_error(path, `invalid data shape node kind: ${(node as any).kind}`);
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
|
|
1864
|
+
export let parse_cirru_edn_as = (code: string, graph: DataShapeGraph): CalcitValue => {
|
|
1865
|
+
if (typeof code !== "string") throw new Error(`parse-cirru-edn-as expected a string, got ${typed_edn_kind(code)}`);
|
|
1866
|
+
if (graph.version !== 1) throw new Error(`parse-cirru-edn-as expected data shape ABI version 1, got ${graph.version}`);
|
|
1867
|
+
if (typeof graph.fingerprint !== "string" || graph.fingerprint.length === 0) {
|
|
1868
|
+
throw new Error("parse-cirru-edn-as expected a non-empty data shape fingerprint");
|
|
1869
|
+
}
|
|
1870
|
+
const enumOptions: CalcitValue[] = [];
|
|
1871
|
+
for (const node of graph.nodes) {
|
|
1872
|
+
if (node.kind === "enum") enumOptions.push(newTag(node.nominal.name()), node.nominal);
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
let input: any;
|
|
1876
|
+
try {
|
|
1877
|
+
const nodes = parse(code);
|
|
1878
|
+
if (nodes.length !== 1) throw new Error(`expected EDN in a single node, got ${nodes.length}`);
|
|
1879
|
+
input = extract_cirru_edn_for_typed(nodes[0], new CalcitSliceMap(enumOptions));
|
|
1880
|
+
} catch (error) {
|
|
1881
|
+
const message = error instanceof Error ? error.message : `${error}`;
|
|
1882
|
+
throw new Error(`parse-cirru-edn-as failed to parse Cirru EDN: ${message}`);
|
|
1883
|
+
}
|
|
1884
|
+
return decode_typed_edn_node(graph, graph.root, input, "$", 0) as CalcitValue;
|
|
1885
|
+
};
|
|
1886
|
+
|
|
1659
1887
|
const json_to_calcit = (value: any): CalcitValue => {
|
|
1660
1888
|
if (value == null) return null;
|
|
1661
1889
|
if (typeof value === "string") return value;
|
package/ts-src/js-cirru.mts
CHANGED
|
@@ -13,6 +13,7 @@ import { CalcitImpl } from "./js-impl.mjs";
|
|
|
13
13
|
import { CalcitRef } from "./js-ref.mjs";
|
|
14
14
|
import { deepEqual } from "@calcit/ternary-tree/lib/utils.mjs";
|
|
15
15
|
import { atom } from "./js-ref.mjs";
|
|
16
|
+
import { TypedEdnSetView } from "./typed-edn.mjs";
|
|
16
17
|
|
|
17
18
|
type CirruEdnFormat = string | CirruEdnFormat[];
|
|
18
19
|
|
|
@@ -256,7 +257,7 @@ const tag_to_string = (tag: CalcitValue): string => {
|
|
|
256
257
|
throw new Error(`Unsupported tag for EDN: ${tag}`);
|
|
257
258
|
};
|
|
258
259
|
|
|
259
|
-
|
|
260
|
+
const extract_cirru_edn_inner = (x: CirruEdnFormat, options: CalcitValue, preserveSourceEntries: boolean): any => {
|
|
260
261
|
if (typeof x === "string") {
|
|
261
262
|
if (x === "nil") {
|
|
262
263
|
return null;
|
|
@@ -291,6 +292,7 @@ export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): Calcit
|
|
|
291
292
|
}
|
|
292
293
|
if (x[0] === "{}") {
|
|
293
294
|
let result: Array<CalcitValue> = [];
|
|
295
|
+
const sourceKeys: CirruEdnFormat[] = [];
|
|
294
296
|
x.forEach((pair, idx) => {
|
|
295
297
|
if (idx === 0) {
|
|
296
298
|
return; // skip first `{}` symbol
|
|
@@ -298,7 +300,15 @@ export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): Calcit
|
|
|
298
300
|
if (pair instanceof Array) {
|
|
299
301
|
if (pair[0] === ";") return;
|
|
300
302
|
if (pair.length === 2) {
|
|
301
|
-
|
|
303
|
+
const key = extract_cirru_edn_inner(pair[0], options, preserveSourceEntries);
|
|
304
|
+
const value = extract_cirru_edn_inner(pair[1], options, preserveSourceEntries);
|
|
305
|
+
const existingIdx = preserveSourceEntries ? sourceKeys.findIndex((sourceKey) => deepEqual(sourceKey, pair[0])) : -1;
|
|
306
|
+
if (existingIdx >= 0) {
|
|
307
|
+
result[(existingIdx << 1) + 1] = value;
|
|
308
|
+
} else {
|
|
309
|
+
if (preserveSourceEntries) sourceKeys.push(pair[0]);
|
|
310
|
+
result.push(key, value);
|
|
311
|
+
}
|
|
302
312
|
} else {
|
|
303
313
|
throw new Error(`Expected a pair, got: ${pair}`);
|
|
304
314
|
}
|
|
@@ -323,7 +333,10 @@ export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): Calcit
|
|
|
323
333
|
if (pair[0] === ";") return;
|
|
324
334
|
if (pair.length === 2) {
|
|
325
335
|
if (typeof pair[0] === "string") {
|
|
326
|
-
entries.push([
|
|
336
|
+
entries.push([
|
|
337
|
+
extractFieldTag(pair[0]),
|
|
338
|
+
extract_cirru_edn_inner(pair[1], options, preserveSourceEntries),
|
|
339
|
+
]);
|
|
327
340
|
} else {
|
|
328
341
|
throw new Error(`Expected string as field, got: ${pair}`);
|
|
329
342
|
}
|
|
@@ -348,10 +361,9 @@ export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): Calcit
|
|
|
348
361
|
if (options instanceof CalcitMap || options instanceof CalcitSliceMap) {
|
|
349
362
|
let v = options.get(extractFieldTag(name));
|
|
350
363
|
if (v != null && v instanceof CalcitRecord) {
|
|
351
|
-
if (
|
|
352
|
-
|
|
364
|
+
if (deepEqual(v.fields, fields)) {
|
|
365
|
+
return new CalcitRecord(extractFieldTag(name), fields, values, v.structRef);
|
|
353
366
|
}
|
|
354
|
-
return new CalcitRecord(extractFieldTag(name), fields, values, v.structRef);
|
|
355
367
|
}
|
|
356
368
|
}
|
|
357
369
|
|
|
@@ -368,19 +380,19 @@ export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): Calcit
|
|
|
368
380
|
x
|
|
369
381
|
.slice(1)
|
|
370
382
|
.filter(notComment)
|
|
371
|
-
.map((x) =>
|
|
383
|
+
.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries))
|
|
372
384
|
);
|
|
373
385
|
}
|
|
374
386
|
if (x[0] === "#{}") {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
);
|
|
387
|
+
const sourceItems = x.slice(1).filter(notComment);
|
|
388
|
+
const uniqueSourceItems = preserveSourceEntries
|
|
389
|
+
? sourceItems.filter((item, idx) => !sourceItems.slice(0, idx).some((existing) => deepEqual(existing, item)))
|
|
390
|
+
: sourceItems;
|
|
391
|
+
const items = uniqueSourceItems.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries));
|
|
392
|
+
return preserveSourceEntries ? new TypedEdnSetView(items) : new CalcitSet(items);
|
|
381
393
|
}
|
|
382
394
|
if (x[0] === "do" && x.length === 2) {
|
|
383
|
-
return
|
|
395
|
+
return extract_cirru_edn_inner(x[1], options, preserveSourceEntries);
|
|
384
396
|
}
|
|
385
397
|
if (x[0] === "quote") {
|
|
386
398
|
if (x.length !== 2) {
|
|
@@ -393,11 +405,11 @@ export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): Calcit
|
|
|
393
405
|
throw new Error(`tuple expects at least 1 value, got: ${x}`);
|
|
394
406
|
}
|
|
395
407
|
return new CalcitTuple(
|
|
396
|
-
|
|
408
|
+
extract_cirru_edn_inner(x[1], options, preserveSourceEntries),
|
|
397
409
|
x
|
|
398
410
|
.slice(2)
|
|
399
411
|
.filter(notComment)
|
|
400
|
-
.map((x) =>
|
|
412
|
+
.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries))
|
|
401
413
|
);
|
|
402
414
|
}
|
|
403
415
|
if (x[0] === "%::") {
|
|
@@ -413,11 +425,11 @@ export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): Calcit
|
|
|
413
425
|
const proto = enumPrototype != null ? unwrap_enum_prototype_local(enumPrototype) : null;
|
|
414
426
|
const enumTag = proto != null ? proto.name.toString() : enumName;
|
|
415
427
|
return new CalcitTuple(
|
|
416
|
-
|
|
428
|
+
extract_cirru_edn_inner(x[2], options, preserveSourceEntries),
|
|
417
429
|
x
|
|
418
430
|
.slice(3)
|
|
419
431
|
.filter(notComment)
|
|
420
|
-
.map((x) =>
|
|
432
|
+
.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries)),
|
|
421
433
|
enumPrototype
|
|
422
434
|
);
|
|
423
435
|
}
|
|
@@ -425,13 +437,21 @@ export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): Calcit
|
|
|
425
437
|
if (x.length !== 2) {
|
|
426
438
|
throw new Error(`atom expects 1 argument, got: ${x}`);
|
|
427
439
|
}
|
|
428
|
-
return atom(
|
|
440
|
+
return atom(extract_cirru_edn_inner(x[1], options, preserveSourceEntries));
|
|
429
441
|
}
|
|
430
442
|
}
|
|
431
443
|
console.error(x);
|
|
432
444
|
throw new Error(`Unexpected data from EDN: ${x}`);
|
|
433
445
|
};
|
|
434
446
|
|
|
447
|
+
export let extract_cirru_edn = (x: CirruEdnFormat, options: CalcitValue): CalcitValue => {
|
|
448
|
+
return extract_cirru_edn_inner(x, options, false) as CalcitValue;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
export let extract_cirru_edn_for_typed = (x: CirruEdnFormat, options: CalcitValue): CalcitValue | TypedEdnSetView => {
|
|
452
|
+
return extract_cirru_edn_inner(x, options, true) as CalcitValue | TypedEdnSetView;
|
|
453
|
+
};
|
|
454
|
+
|
|
435
455
|
export let format_cirru_edn = (data: CalcitValue, useInline: boolean = true): string => {
|
|
436
456
|
if (data == null) {
|
|
437
457
|
return "\ndo nil" + "\n";
|
package/ts-src/js-impl.mts
CHANGED
|
@@ -3,6 +3,8 @@ import { CalcitValue } from "./js-primes.mjs";
|
|
|
3
3
|
import { CalcitTag, castTag, findInFields, toString } from "./calcit-data.mjs";
|
|
4
4
|
import type { CalcitTrait } from "./js-trait.mjs";
|
|
5
5
|
|
|
6
|
+
const CALCIT_IMPL_BRAND = Symbol.for("@calcit/procs/CalcitImpl");
|
|
7
|
+
|
|
6
8
|
export class CalcitImpl {
|
|
7
9
|
name: CalcitTag;
|
|
8
10
|
origin: CalcitTrait | null;
|
|
@@ -10,7 +12,38 @@ export class CalcitImpl {
|
|
|
10
12
|
values: Array<CalcitValue>;
|
|
11
13
|
cachedHash: Hash;
|
|
12
14
|
|
|
15
|
+
static [Symbol.hasInstance](value: unknown): boolean {
|
|
16
|
+
if (typeof value !== "object" || value === null) return false;
|
|
17
|
+
|
|
18
|
+
const candidate = value as Record<PropertyKey, unknown>;
|
|
19
|
+
const brand = Object.getOwnPropertyDescriptor(candidate, CALCIT_IMPL_BRAND);
|
|
20
|
+
const name = Object.getOwnPropertyDescriptor(candidate, "name");
|
|
21
|
+
const origin = Object.getOwnPropertyDescriptor(candidate, "origin");
|
|
22
|
+
const fields = Object.getOwnPropertyDescriptor(candidate, "fields");
|
|
23
|
+
const values = Object.getOwnPropertyDescriptor(candidate, "values");
|
|
24
|
+
const cachedHash = Object.getOwnPropertyDescriptor(candidate, "cachedHash");
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
brand?.value === true &&
|
|
28
|
+
name !== undefined &&
|
|
29
|
+
origin !== undefined &&
|
|
30
|
+
fields !== undefined &&
|
|
31
|
+
values !== undefined &&
|
|
32
|
+
cachedHash !== undefined &&
|
|
33
|
+
typeof name.value === "object" &&
|
|
34
|
+
name.value !== null &&
|
|
35
|
+
typeof (name.value as { value?: unknown }).value === "string" &&
|
|
36
|
+
(origin.value === null || typeof origin.value === "object") &&
|
|
37
|
+
Array.isArray(fields.value) &&
|
|
38
|
+
Array.isArray(values.value)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
13
42
|
constructor(name: CalcitTag, fields: Array<CalcitTag>, values: Array<CalcitValue>, origin: CalcitTrait | null = null) {
|
|
43
|
+
// Vite can temporarily retain two copies of @calcit/procs while refreshing
|
|
44
|
+
// optimized dependencies. A global, non-enumerable brand keeps impl values
|
|
45
|
+
// recognizable across those otherwise distinct module instances.
|
|
46
|
+
Object.defineProperty(this, CALCIT_IMPL_BRAND, { value: true });
|
|
14
47
|
this.name = name;
|
|
15
48
|
this.origin = origin;
|
|
16
49
|
this.fields = fields;
|