@calcit/procs 0.12.59 → 0.13.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/.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/editing-history/202608061340-release-0.13.1.md +6 -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/history/202608061335-recursive-nominal-display.md +28 -0
- package/lib/calcit.procs.d.mts +35 -4
- package/lib/calcit.procs.mjs +233 -11
- package/lib/custom-formatter.mjs +28 -2
- package/lib/js-cirru.d.mts +2 -0
- package/lib/js-cirru.mjs +39 -17
- package/lib/js-enum.mjs +1 -1
- package/lib/js-impl.d.mts +1 -0
- package/lib/js-impl.mjs +28 -0
- package/lib/js-record.mjs +1 -1
- package/lib/js-struct.mjs +1 -1
- 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/custom-formatter.mts +55 -2
- package/ts-src/js-cirru.mts +39 -19
- package/ts-src/js-enum.mts +1 -1
- package/ts-src/js-impl.mts +33 -0
- package/ts-src/js-record.mts +1 -1
- package/ts-src/js-struct.mts +1 -1
- package/ts-src/typed-edn.mts +7 -0
package/lib/calcit.procs.mjs
CHANGED
|
@@ -4,7 +4,7 @@ export const calcit_package_json = pkg;
|
|
|
4
4
|
import { parse } from "@cirru/parser.ts";
|
|
5
5
|
import { writeCirruCode } from "@cirru/writer.ts";
|
|
6
6
|
import { CalcitSymbol, CalcitTag, CalcitRecur, castTag, newTag, refsRegistry, toString, getStringName, _$n__$e_, hashFunction, } from "./calcit-data.mjs";
|
|
7
|
-
import { CalcitRef } from "./js-ref.mjs";
|
|
7
|
+
import { CalcitRef, atom } from "./js-ref.mjs";
|
|
8
8
|
import { CalcitRecord } from "./js-record.mjs";
|
|
9
9
|
import { CalcitImpl } from "./js-impl.mjs";
|
|
10
10
|
import { CalcitStruct } from "./js-struct.mjs";
|
|
@@ -31,7 +31,8 @@ import { CalcitList, CalcitSliceList, foldl } from "./js-list.mjs";
|
|
|
31
31
|
import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
|
|
32
32
|
import { CalcitSet } from "./js-set.mjs";
|
|
33
33
|
import { CalcitTuple } from "./js-tuple.mjs";
|
|
34
|
-
import { to_calcit_data, extract_cirru_edn, CalcitCirruQuote } from "./js-cirru.mjs";
|
|
34
|
+
import { to_calcit_data, extract_cirru_edn, extract_cirru_edn_for_typed, CalcitCirruQuote } from "./js-cirru.mjs";
|
|
35
|
+
import { TypedEdnSetView } from "./typed-edn.mjs";
|
|
35
36
|
let inNodeJs = typeof process !== "undefined" && process?.release?.name === "node";
|
|
36
37
|
export let type_of = (x) => {
|
|
37
38
|
if (typeof x === "string") {
|
|
@@ -256,7 +257,7 @@ export let _$n_impl_$o__$o_new = (name, ...pairs) => {
|
|
|
256
257
|
let sourcePairs = pairs;
|
|
257
258
|
if (pairs.length === 1 && pairs[0] instanceof CalcitImpl) {
|
|
258
259
|
const sourceImpl = pairs[0];
|
|
259
|
-
sourcePairs = sourceImpl.fields.map((field, idx) => new CalcitTuple(field, [sourceImpl.values[idx]], null));
|
|
260
|
+
sourcePairs = sourceImpl.fields.map((field, idx) => new CalcitTuple(newTag(field.value), [sourceImpl.values[idx]], null));
|
|
260
261
|
}
|
|
261
262
|
for (let idx = 0; idx < sourcePairs.length; idx++) {
|
|
262
263
|
const pairValue = sourcePairs[idx];
|
|
@@ -856,6 +857,16 @@ export let _$n_list_$o_first = (xs) => {
|
|
|
856
857
|
console.error(xs);
|
|
857
858
|
throw new Error("Expected a list");
|
|
858
859
|
};
|
|
860
|
+
export let _$n_list_$o_last = (xs) => {
|
|
861
|
+
if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
|
|
862
|
+
if (xs.isEmpty()) {
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
return xs.get(xs.len() - 1);
|
|
866
|
+
}
|
|
867
|
+
console.error(xs);
|
|
868
|
+
throw new Error("Expected a list");
|
|
869
|
+
};
|
|
859
870
|
export let _$n_str_$o_first = (xs) => {
|
|
860
871
|
if (typeof xs === "string") {
|
|
861
872
|
return xs[0];
|
|
@@ -897,7 +908,7 @@ export let timeout_call = (duration, f) => {
|
|
|
897
908
|
export let _$n_list_$o_rest = (xs) => {
|
|
898
909
|
if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
|
|
899
910
|
if (xs.len() === 0) {
|
|
900
|
-
return
|
|
911
|
+
return xs.slice(0, 0);
|
|
901
912
|
}
|
|
902
913
|
return xs.rest();
|
|
903
914
|
}
|
|
@@ -947,7 +958,7 @@ export let last = (xs) => {
|
|
|
947
958
|
export let butlast = (xs) => {
|
|
948
959
|
if (xs instanceof CalcitList || xs instanceof CalcitSliceList) {
|
|
949
960
|
if (xs.len() === 0) {
|
|
950
|
-
return
|
|
961
|
+
return xs.slice(0, 0);
|
|
951
962
|
}
|
|
952
963
|
return xs.slice(0, xs.len() - 1);
|
|
953
964
|
}
|
|
@@ -1234,12 +1245,12 @@ export let _$n_str_$o_slice = (xs, m, n) => {
|
|
|
1234
1245
|
export let _$n_str_$o_find_index = (x, y) => {
|
|
1235
1246
|
return x.indexOf(y);
|
|
1236
1247
|
};
|
|
1237
|
-
export let
|
|
1238
|
-
|
|
1239
|
-
if (Number.isNaN(value)) {
|
|
1248
|
+
export let _$n_parse_float = (x) => {
|
|
1249
|
+
if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(x)) {
|
|
1240
1250
|
return null;
|
|
1241
1251
|
}
|
|
1242
|
-
|
|
1252
|
+
const value = Number(x);
|
|
1253
|
+
return Number.isNaN(value) ? null : value;
|
|
1243
1254
|
};
|
|
1244
1255
|
export let trim = (x, c) => {
|
|
1245
1256
|
if (c != null) {
|
|
@@ -1304,7 +1315,7 @@ export let js_set = aset;
|
|
|
1304
1315
|
export let js_delete = (obj, name) => {
|
|
1305
1316
|
return delete obj[name];
|
|
1306
1317
|
};
|
|
1307
|
-
export let
|
|
1318
|
+
export let _$n_get_env = (name, v0) => {
|
|
1308
1319
|
let v = undefined;
|
|
1309
1320
|
if (inNodeJs) {
|
|
1310
1321
|
// only available for Node.js
|
|
@@ -1319,8 +1330,12 @@ export let get_env = (name, v0) => {
|
|
|
1319
1330
|
if (v == null && v0 == null) {
|
|
1320
1331
|
console.warn(`(get-env "${name}"): config not found`);
|
|
1321
1332
|
}
|
|
1322
|
-
return v ?? v0;
|
|
1333
|
+
return v ?? v0 ?? null;
|
|
1323
1334
|
};
|
|
1335
|
+
// Keep already-emitted JS bundles loadable across the nominal Option migration.
|
|
1336
|
+
// Newly generated code calls the internal raw proc; source-level `get-env`
|
|
1337
|
+
// remains the typed Option wrapper defined in calcit.core.
|
|
1338
|
+
export let get_env = _$n_get_env;
|
|
1324
1339
|
export let turn_tag = (x) => {
|
|
1325
1340
|
if (typeof x === "string") {
|
|
1326
1341
|
return newTag(x);
|
|
@@ -1525,6 +1540,213 @@ export let parse_cirru_edn = (code, options) => {
|
|
|
1525
1540
|
throw new Error(`Expected EDN in a single node, got ${nodes.length}`);
|
|
1526
1541
|
}
|
|
1527
1542
|
};
|
|
1543
|
+
const typed_edn_kind = (value) => {
|
|
1544
|
+
if (value == null)
|
|
1545
|
+
return "nil";
|
|
1546
|
+
if (typeof value === "boolean")
|
|
1547
|
+
return "bool";
|
|
1548
|
+
if (typeof value === "number")
|
|
1549
|
+
return "number";
|
|
1550
|
+
if (typeof value === "string")
|
|
1551
|
+
return "string";
|
|
1552
|
+
if (value instanceof CalcitSymbol)
|
|
1553
|
+
return "symbol";
|
|
1554
|
+
if (value instanceof CalcitTag)
|
|
1555
|
+
return "tag";
|
|
1556
|
+
if (value instanceof CalcitList || value instanceof CalcitSliceList)
|
|
1557
|
+
return "list";
|
|
1558
|
+
if (value instanceof CalcitMap || value instanceof CalcitSliceMap)
|
|
1559
|
+
return "map";
|
|
1560
|
+
if (value instanceof CalcitSet || value instanceof TypedEdnSetView)
|
|
1561
|
+
return "set";
|
|
1562
|
+
if (value instanceof CalcitRecord)
|
|
1563
|
+
return "record";
|
|
1564
|
+
if (value instanceof CalcitTuple)
|
|
1565
|
+
return value.enumPrototype == null ? "tuple" : "enum";
|
|
1566
|
+
if (value instanceof CalcitRef)
|
|
1567
|
+
return "atom";
|
|
1568
|
+
if (value instanceof CalcitCirruQuote)
|
|
1569
|
+
return "cirru-quote";
|
|
1570
|
+
if (value instanceof Uint8Array)
|
|
1571
|
+
return "buffer";
|
|
1572
|
+
return "unsupported";
|
|
1573
|
+
};
|
|
1574
|
+
const typed_edn_error = (path, message) => {
|
|
1575
|
+
throw new Error(`parse-cirru-edn-as failed at ${path}: ${message}`);
|
|
1576
|
+
};
|
|
1577
|
+
const enum_prototype_name = (value) => {
|
|
1578
|
+
return value instanceof CalcitEnum ? value.name() : value.name.value;
|
|
1579
|
+
};
|
|
1580
|
+
const decode_typed_edn_node = (graph, nodeId, input, path, depth) => {
|
|
1581
|
+
if (depth > 1024)
|
|
1582
|
+
typed_edn_error(path, "decode nesting exceeds 1024");
|
|
1583
|
+
const node = graph.nodes[nodeId];
|
|
1584
|
+
if (node == null)
|
|
1585
|
+
typed_edn_error(path, `invalid data shape node #${nodeId}`);
|
|
1586
|
+
switch (node.kind) {
|
|
1587
|
+
case "unit":
|
|
1588
|
+
if (input == null)
|
|
1589
|
+
return null;
|
|
1590
|
+
return typed_edn_error(path, `expected nil, got ${typed_edn_kind(input)}`);
|
|
1591
|
+
case "bool":
|
|
1592
|
+
if (typeof input === "boolean")
|
|
1593
|
+
return input;
|
|
1594
|
+
return typed_edn_error(path, `expected bool, got ${typed_edn_kind(input)}`);
|
|
1595
|
+
case "number":
|
|
1596
|
+
if (typeof input === "number")
|
|
1597
|
+
return input;
|
|
1598
|
+
return typed_edn_error(path, `expected number, got ${typed_edn_kind(input)}`);
|
|
1599
|
+
case "string":
|
|
1600
|
+
if (typeof input === "string")
|
|
1601
|
+
return input;
|
|
1602
|
+
return typed_edn_error(path, `expected string, got ${typed_edn_kind(input)}`);
|
|
1603
|
+
case "symbol":
|
|
1604
|
+
if (input instanceof CalcitSymbol)
|
|
1605
|
+
return input;
|
|
1606
|
+
return typed_edn_error(path, `expected symbol, got ${typed_edn_kind(input)}`);
|
|
1607
|
+
case "tag":
|
|
1608
|
+
if (input instanceof CalcitTag)
|
|
1609
|
+
return input;
|
|
1610
|
+
return typed_edn_error(path, `expected tag, got ${typed_edn_kind(input)}`);
|
|
1611
|
+
case "buffer":
|
|
1612
|
+
if (input instanceof Uint8Array)
|
|
1613
|
+
return input;
|
|
1614
|
+
return typed_edn_error(path, `expected buffer, got ${typed_edn_kind(input)}`);
|
|
1615
|
+
case "cirru-quote":
|
|
1616
|
+
if (input instanceof CalcitCirruQuote)
|
|
1617
|
+
return input;
|
|
1618
|
+
return typed_edn_error(path, `expected cirru-quote, got ${typed_edn_kind(input)}`);
|
|
1619
|
+
case "optional":
|
|
1620
|
+
return input == null ? null : decode_typed_edn_node(graph, node.inner, input, path, depth + 1);
|
|
1621
|
+
case "list": {
|
|
1622
|
+
if (!(input instanceof CalcitList || input instanceof CalcitSliceList)) {
|
|
1623
|
+
return typed_edn_error(path, `expected list, got ${typed_edn_kind(input)}`);
|
|
1624
|
+
}
|
|
1625
|
+
const values = Array.from(input.items()).map((value, idx) => decode_typed_edn_node(graph, node.inner, value, `${path}[${idx}]`, depth + 1));
|
|
1626
|
+
return new CalcitSliceList(values);
|
|
1627
|
+
}
|
|
1628
|
+
case "set": {
|
|
1629
|
+
if (!(input instanceof CalcitSet || input instanceof TypedEdnSetView)) {
|
|
1630
|
+
return typed_edn_error(path, `expected set, got ${typed_edn_kind(input)}`);
|
|
1631
|
+
}
|
|
1632
|
+
const values = [];
|
|
1633
|
+
const inputValues = input instanceof TypedEdnSetView ? input.items : input.values();
|
|
1634
|
+
inputValues.forEach((value) => {
|
|
1635
|
+
const itemPath = `${path}.item`;
|
|
1636
|
+
const decoded = decode_typed_edn_node(graph, node.inner, value, itemPath, depth + 1);
|
|
1637
|
+
if (values.some((existing) => _$n__$e_(existing, decoded))) {
|
|
1638
|
+
typed_edn_error(itemPath, "duplicate decoded set value");
|
|
1639
|
+
}
|
|
1640
|
+
values.push(decoded);
|
|
1641
|
+
});
|
|
1642
|
+
return new CalcitSet(values);
|
|
1643
|
+
}
|
|
1644
|
+
case "map": {
|
|
1645
|
+
if (!(input instanceof CalcitMap || input instanceof CalcitSliceMap)) {
|
|
1646
|
+
return typed_edn_error(path, `expected map, got ${typed_edn_kind(input)}`);
|
|
1647
|
+
}
|
|
1648
|
+
const entries = [];
|
|
1649
|
+
input.pairs().forEach(([key, value]) => {
|
|
1650
|
+
const keyPath = `${path}.key`;
|
|
1651
|
+
const decodedKey = decode_typed_edn_node(graph, node.key, key, keyPath, depth + 1);
|
|
1652
|
+
for (let idx = 0; idx < entries.length; idx += 2) {
|
|
1653
|
+
if (_$n__$e_(entries[idx], decodedKey)) {
|
|
1654
|
+
typed_edn_error(keyPath, "duplicate decoded map key");
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
const decodedValue = decode_typed_edn_node(graph, node.value, value, `${path}.value`, depth + 1);
|
|
1658
|
+
entries.push(decodedKey, decodedValue);
|
|
1659
|
+
});
|
|
1660
|
+
return new CalcitSliceMap(entries);
|
|
1661
|
+
}
|
|
1662
|
+
case "ref":
|
|
1663
|
+
if (!(input instanceof CalcitRef))
|
|
1664
|
+
return typed_edn_error(path, `expected atom, got ${typed_edn_kind(input)}`);
|
|
1665
|
+
return atom(decode_typed_edn_node(graph, node.inner, input.value, `${path}.value`, depth + 1));
|
|
1666
|
+
case "struct": {
|
|
1667
|
+
if (!(input instanceof CalcitRecord)) {
|
|
1668
|
+
return typed_edn_error(path, `expected record :${node.nominal.name.value}, got ${typed_edn_kind(input)}`);
|
|
1669
|
+
}
|
|
1670
|
+
if (input.name.value !== node.nominal.name.value) {
|
|
1671
|
+
return typed_edn_error(path, `expected record :${node.nominal.name.value}, got record :${input.name.value}`);
|
|
1672
|
+
}
|
|
1673
|
+
const expectedNames = node.fields.map(([name]) => name);
|
|
1674
|
+
const actualNames = input.fields.map((field) => field.value);
|
|
1675
|
+
const missing = expectedNames.filter((name) => !actualNames.includes(name)).sort();
|
|
1676
|
+
const unknown = actualNames.filter((name) => !expectedNames.includes(name)).sort();
|
|
1677
|
+
if (missing.length > 0 || unknown.length > 0 || expectedNames.length !== actualNames.length) {
|
|
1678
|
+
return typed_edn_error(path, `record :${node.nominal.name.value} fields mismatch; missing [${missing.join(", ")}], unknown [${unknown.join(", ")}]`);
|
|
1679
|
+
}
|
|
1680
|
+
const decodedFields = new Map();
|
|
1681
|
+
node.fields.forEach(([name, fieldNode]) => {
|
|
1682
|
+
const idx = actualNames.indexOf(name);
|
|
1683
|
+
decodedFields.set(name, decode_typed_edn_node(graph, fieldNode, input.values[idx], `${path}.${name}`, depth + 1));
|
|
1684
|
+
});
|
|
1685
|
+
// Native declarations use lexical field order while the JS runtime keeps
|
|
1686
|
+
// its interned-tag order. Re-align values to the nominal JS declaration.
|
|
1687
|
+
if (decodedFields.size !== node.nominal.fields.length) {
|
|
1688
|
+
return typed_edn_error(path, `record :${node.nominal.name.value} decoder fields do not match its nominal declaration`);
|
|
1689
|
+
}
|
|
1690
|
+
const values = node.nominal.fields.map((field) => {
|
|
1691
|
+
if (!decodedFields.has(field.value)) {
|
|
1692
|
+
return typed_edn_error(path, `record :${node.nominal.name.value} is missing declared field :${field.value}`);
|
|
1693
|
+
}
|
|
1694
|
+
return decodedFields.get(field.value);
|
|
1695
|
+
});
|
|
1696
|
+
return new CalcitRecord(node.nominal.name, node.nominal.fields, values, node.nominal);
|
|
1697
|
+
}
|
|
1698
|
+
case "enum": {
|
|
1699
|
+
if (!(input instanceof CalcitTuple) || input.enumPrototype == null) {
|
|
1700
|
+
return typed_edn_error(path, `expected enum :${node.nominal.name()}, got ${typed_edn_kind(input)}`);
|
|
1701
|
+
}
|
|
1702
|
+
const actualEnumName = enum_prototype_name(input.enumPrototype);
|
|
1703
|
+
if (actualEnumName !== node.nominal.name()) {
|
|
1704
|
+
return typed_edn_error(path, `expected enum :${node.nominal.name()}, got enum :${actualEnumName}`);
|
|
1705
|
+
}
|
|
1706
|
+
if (!(input.tag instanceof CalcitTag)) {
|
|
1707
|
+
return typed_edn_error(path, `enum :${node.nominal.name()} variant must be a tag`);
|
|
1708
|
+
}
|
|
1709
|
+
const inputTag = input.tag;
|
|
1710
|
+
const variant = node.variants.find((candidate) => candidate.tag === inputTag.value);
|
|
1711
|
+
if (variant == null) {
|
|
1712
|
+
return typed_edn_error(path, `enum :${node.nominal.name()} has no variant :${inputTag.value}`);
|
|
1713
|
+
}
|
|
1714
|
+
if (variant.payload.length !== input.extra.length) {
|
|
1715
|
+
return typed_edn_error(path, `enum :${node.nominal.name()} variant :${variant.tag} expects ${variant.payload.length} payload(s), got ${input.extra.length}`);
|
|
1716
|
+
}
|
|
1717
|
+
const values = variant.payload.map((payloadNode, idx) => decode_typed_edn_node(graph, payloadNode, input.extra[idx], `${path}.payload[${idx}]`, depth + 1));
|
|
1718
|
+
return new CalcitTuple(newTag(variant.tag), values, node.nominal);
|
|
1719
|
+
}
|
|
1720
|
+
default:
|
|
1721
|
+
return typed_edn_error(path, `invalid data shape node kind: ${node.kind}`);
|
|
1722
|
+
}
|
|
1723
|
+
};
|
|
1724
|
+
export let parse_cirru_edn_as = (code, graph) => {
|
|
1725
|
+
if (typeof code !== "string")
|
|
1726
|
+
throw new Error(`parse-cirru-edn-as expected a string, got ${typed_edn_kind(code)}`);
|
|
1727
|
+
if (graph.version !== 1)
|
|
1728
|
+
throw new Error(`parse-cirru-edn-as expected data shape ABI version 1, got ${graph.version}`);
|
|
1729
|
+
if (typeof graph.fingerprint !== "string" || graph.fingerprint.length === 0) {
|
|
1730
|
+
throw new Error("parse-cirru-edn-as expected a non-empty data shape fingerprint");
|
|
1731
|
+
}
|
|
1732
|
+
const enumOptions = [];
|
|
1733
|
+
for (const node of graph.nodes) {
|
|
1734
|
+
if (node.kind === "enum")
|
|
1735
|
+
enumOptions.push(newTag(node.nominal.name()), node.nominal);
|
|
1736
|
+
}
|
|
1737
|
+
let input;
|
|
1738
|
+
try {
|
|
1739
|
+
const nodes = parse(code);
|
|
1740
|
+
if (nodes.length !== 1)
|
|
1741
|
+
throw new Error(`expected EDN in a single node, got ${nodes.length}`);
|
|
1742
|
+
input = extract_cirru_edn_for_typed(nodes[0], new CalcitSliceMap(enumOptions));
|
|
1743
|
+
}
|
|
1744
|
+
catch (error) {
|
|
1745
|
+
const message = error instanceof Error ? error.message : `${error}`;
|
|
1746
|
+
throw new Error(`parse-cirru-edn-as failed to parse Cirru EDN: ${message}`);
|
|
1747
|
+
}
|
|
1748
|
+
return decode_typed_edn_node(graph, graph.root, input, "$", 0);
|
|
1749
|
+
};
|
|
1528
1750
|
const json_to_calcit = (value) => {
|
|
1529
1751
|
if (value == null)
|
|
1530
1752
|
return null;
|
package/lib/custom-formatter.mjs
CHANGED
|
@@ -2,6 +2,8 @@ import { isLiteral } from "./js-primes.mjs";
|
|
|
2
2
|
import { CalcitSymbol, CalcitTag } from "./calcit-data.mjs";
|
|
3
3
|
import { CalcitRef } from "./js-ref.mjs";
|
|
4
4
|
import { CalcitRecord } from "./js-record.mjs";
|
|
5
|
+
import { CalcitStruct } from "./js-struct.mjs";
|
|
6
|
+
import { CalcitEnum } from "./js-enum.mjs";
|
|
5
7
|
import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
|
|
6
8
|
import { CalcitList, CalcitSliceList } from "./js-list.mjs";
|
|
7
9
|
import { CalcitSet } from "./js-set.mjs";
|
|
@@ -149,15 +151,22 @@ export let load_console_formatter_$x_ = () => {
|
|
|
149
151
|
}, `${obj.len()}`), preview);
|
|
150
152
|
}
|
|
151
153
|
if (obj instanceof CalcitRecord) {
|
|
154
|
+
let name = new CalcitSymbol(obj.name.value);
|
|
152
155
|
if (obj.structRef.impls.length > 0) {
|
|
153
|
-
let ret = div({ color: hsl(280, 80, 60, 0.4), maxWidth: "100%" }, span({}, "%{}"), span({ marginLeft: "6px" }, embedObject(obj.structRef.impls[0])), span({ marginLeft: "6px" }, embedObject(
|
|
156
|
+
let ret = div({ color: hsl(280, 80, 60, 0.4), maxWidth: "100%" }, span({}, "%{}"), span({ marginLeft: "6px" }, embedObject(obj.structRef.impls[0])), span({ marginLeft: "6px" }, embedObject(name)), span({ marginLeft: "6px" }, `...`));
|
|
154
157
|
return ret;
|
|
155
158
|
}
|
|
156
159
|
else {
|
|
157
|
-
let ret = div({ color: hsl(280, 80, 60, 0.4), maxWidth: "100%" },
|
|
160
|
+
let ret = div({ color: hsl(280, 80, 60, 0.4), maxWidth: "100%" }, "%{} ", embedObject(name), " ...");
|
|
158
161
|
return ret;
|
|
159
162
|
}
|
|
160
163
|
}
|
|
164
|
+
if (obj instanceof CalcitStruct) {
|
|
165
|
+
return div({ color: hsl(280, 80, 60, 0.4), maxWidth: "100%" }, "%struct ", embedObject(new CalcitSymbol(obj.name.value)), " ...");
|
|
166
|
+
}
|
|
167
|
+
if (obj instanceof CalcitEnum) {
|
|
168
|
+
return div({ color: hsl(280, 80, 60, 0.4), maxWidth: "100%" }, "%enum ", embedObject(new CalcitSymbol(obj.prototype.name.value)), " ...");
|
|
169
|
+
}
|
|
161
170
|
if (obj instanceof CalcitTuple) {
|
|
162
171
|
if (obj.impls.length > 0) {
|
|
163
172
|
let ret = div({ marginRight: "16px" }, div({ display: "inline-block", color: hsl(300, 100, 40) }, "%::"), div({ marginLeft: "6px", display: "inline-block" }, embedObject(obj.impls[0])), div({ marginLeft: "6px", display: "inline-block" }, embedObject(obj.tag)));
|
|
@@ -200,6 +209,9 @@ export let load_console_formatter_$x_ = () => {
|
|
|
200
209
|
if (obj instanceof CalcitRecord) {
|
|
201
210
|
return obj.fields.length > 0;
|
|
202
211
|
}
|
|
212
|
+
if (obj instanceof CalcitStruct || obj instanceof CalcitEnum) {
|
|
213
|
+
return obj instanceof CalcitStruct ? obj.fields.length > 0 : obj.prototype.fields.length > 0;
|
|
214
|
+
}
|
|
203
215
|
return false;
|
|
204
216
|
},
|
|
205
217
|
body: (obj, config) => {
|
|
@@ -255,6 +267,20 @@ export let load_console_formatter_$x_ = () => {
|
|
|
255
267
|
}
|
|
256
268
|
return ret;
|
|
257
269
|
}
|
|
270
|
+
if (obj instanceof CalcitStruct) {
|
|
271
|
+
let ret = table({ color: hsl(280, 80, 60), borderLeft: "1px solid #eee" });
|
|
272
|
+
for (let idx = 0; idx < obj.fields.length; idx++) {
|
|
273
|
+
ret.push(tr({}, td({ paddingLeft: "8px", verticalAlign: "top", whiteSpace: "pre", minWidth: "40px" }, embedObject(obj.fields[idx])), td({ paddingLeft: "8px" }, embedObject(obj.fieldTypes[idx]))));
|
|
274
|
+
}
|
|
275
|
+
return ret;
|
|
276
|
+
}
|
|
277
|
+
if (obj instanceof CalcitEnum) {
|
|
278
|
+
let ret = table({ color: hsl(280, 80, 60), borderLeft: "1px solid #eee" });
|
|
279
|
+
for (let idx = 0; idx < obj.prototype.fields.length; idx++) {
|
|
280
|
+
ret.push(tr({}, td({ paddingLeft: "8px", verticalAlign: "top", whiteSpace: "pre", minWidth: "40px" }, embedObject(obj.prototype.fields[idx])), td({ paddingLeft: "8px" }, embedObject(obj.prototype.values[idx]))));
|
|
281
|
+
}
|
|
282
|
+
return ret;
|
|
283
|
+
}
|
|
258
284
|
return null;
|
|
259
285
|
},
|
|
260
286
|
},
|
package/lib/js-cirru.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CirruWriterNode } from "@cirru/writer.ts";
|
|
2
2
|
import { CalcitValue } from "./js-primes.mjs";
|
|
3
3
|
import { CalcitList } from "./js-list.mjs";
|
|
4
|
+
import { TypedEdnSetView } from "./typed-edn.mjs";
|
|
4
5
|
type CirruEdnFormat = string | CirruEdnFormat[];
|
|
5
6
|
export declare class CalcitCirruQuote {
|
|
6
7
|
value: CirruWriterNode[];
|
|
@@ -16,6 +17,7 @@ export declare let format_cirru_one_liner: (data: CalcitCirruQuote | CalcitList)
|
|
|
16
17
|
/** better use string version of Cirru EDN in future */
|
|
17
18
|
export declare let to_cirru_edn: (x: CalcitValue) => CirruEdnFormat;
|
|
18
19
|
export declare let extract_cirru_edn: (x: CirruEdnFormat, options: CalcitValue) => CalcitValue;
|
|
20
|
+
export declare let extract_cirru_edn_for_typed: (x: CirruEdnFormat, options: CalcitValue) => CalcitValue | TypedEdnSetView;
|
|
19
21
|
export declare let format_cirru_edn: (data: CalcitValue, useInline?: boolean) => string;
|
|
20
22
|
export declare let to_calcit_data: (x: any, noKeyword?: boolean) => CalcitValue;
|
|
21
23
|
/** deep compare cirru array */
|
package/lib/js-cirru.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { CalcitImpl } from "./js-impl.mjs";
|
|
|
11
11
|
import { CalcitRef } from "./js-ref.mjs";
|
|
12
12
|
import { deepEqual } from "@calcit/ternary-tree/lib/utils.mjs";
|
|
13
13
|
import { atom } from "./js-ref.mjs";
|
|
14
|
+
import { TypedEdnSetView } from "./typed-edn.mjs";
|
|
14
15
|
export class CalcitCirruQuote {
|
|
15
16
|
constructor(value) {
|
|
16
17
|
if (value == null) {
|
|
@@ -260,7 +261,7 @@ const tag_to_string = (tag) => {
|
|
|
260
261
|
return tag.name.toString();
|
|
261
262
|
throw new Error(`Unsupported tag for EDN: ${tag}`);
|
|
262
263
|
};
|
|
263
|
-
|
|
264
|
+
const extract_cirru_edn_inner = (x, options, preserveSourceEntries) => {
|
|
264
265
|
if (typeof x === "string") {
|
|
265
266
|
if (x === "nil") {
|
|
266
267
|
return null;
|
|
@@ -295,6 +296,7 @@ export let extract_cirru_edn = (x, options) => {
|
|
|
295
296
|
}
|
|
296
297
|
if (x[0] === "{}") {
|
|
297
298
|
let result = [];
|
|
299
|
+
const sourceKeys = [];
|
|
298
300
|
x.forEach((pair, idx) => {
|
|
299
301
|
if (idx === 0) {
|
|
300
302
|
return; // skip first `{}` symbol
|
|
@@ -303,7 +305,17 @@ export let extract_cirru_edn = (x, options) => {
|
|
|
303
305
|
if (pair[0] === ";")
|
|
304
306
|
return;
|
|
305
307
|
if (pair.length === 2) {
|
|
306
|
-
|
|
308
|
+
const key = extract_cirru_edn_inner(pair[0], options, preserveSourceEntries);
|
|
309
|
+
const value = extract_cirru_edn_inner(pair[1], options, preserveSourceEntries);
|
|
310
|
+
const existingIdx = preserveSourceEntries ? sourceKeys.findIndex((sourceKey) => deepEqual(sourceKey, pair[0])) : -1;
|
|
311
|
+
if (existingIdx >= 0) {
|
|
312
|
+
result[(existingIdx << 1) + 1] = value;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
if (preserveSourceEntries)
|
|
316
|
+
sourceKeys.push(pair[0]);
|
|
317
|
+
result.push(key, value);
|
|
318
|
+
}
|
|
307
319
|
}
|
|
308
320
|
else {
|
|
309
321
|
throw new Error(`Expected a pair, got: ${pair}`);
|
|
@@ -331,7 +343,10 @@ export let extract_cirru_edn = (x, options) => {
|
|
|
331
343
|
return;
|
|
332
344
|
if (pair.length === 2) {
|
|
333
345
|
if (typeof pair[0] === "string") {
|
|
334
|
-
entries.push([
|
|
346
|
+
entries.push([
|
|
347
|
+
extractFieldTag(pair[0]),
|
|
348
|
+
extract_cirru_edn_inner(pair[1], options, preserveSourceEntries),
|
|
349
|
+
]);
|
|
335
350
|
}
|
|
336
351
|
else {
|
|
337
352
|
throw new Error(`Expected string as field, got: ${pair}`);
|
|
@@ -357,10 +372,9 @@ export let extract_cirru_edn = (x, options) => {
|
|
|
357
372
|
if (options instanceof CalcitMap || options instanceof CalcitSliceMap) {
|
|
358
373
|
let v = options.get(extractFieldTag(name));
|
|
359
374
|
if (v != null && v instanceof CalcitRecord) {
|
|
360
|
-
if (
|
|
361
|
-
|
|
375
|
+
if (deepEqual(v.fields, fields)) {
|
|
376
|
+
return new CalcitRecord(extractFieldTag(name), fields, values, v.structRef);
|
|
362
377
|
}
|
|
363
|
-
return new CalcitRecord(extractFieldTag(name), fields, values, v.structRef);
|
|
364
378
|
}
|
|
365
379
|
}
|
|
366
380
|
return new CalcitRecord(extractFieldTag(name), fields, values);
|
|
@@ -375,16 +389,18 @@ export let extract_cirru_edn = (x, options) => {
|
|
|
375
389
|
return new CalcitSliceList(x
|
|
376
390
|
.slice(1)
|
|
377
391
|
.filter(notComment)
|
|
378
|
-
.map((x) =>
|
|
392
|
+
.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries)));
|
|
379
393
|
}
|
|
380
394
|
if (x[0] === "#{}") {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
.filter(
|
|
384
|
-
|
|
395
|
+
const sourceItems = x.slice(1).filter(notComment);
|
|
396
|
+
const uniqueSourceItems = preserveSourceEntries
|
|
397
|
+
? sourceItems.filter((item, idx) => !sourceItems.slice(0, idx).some((existing) => deepEqual(existing, item)))
|
|
398
|
+
: sourceItems;
|
|
399
|
+
const items = uniqueSourceItems.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries));
|
|
400
|
+
return preserveSourceEntries ? new TypedEdnSetView(items) : new CalcitSet(items);
|
|
385
401
|
}
|
|
386
402
|
if (x[0] === "do" && x.length === 2) {
|
|
387
|
-
return
|
|
403
|
+
return extract_cirru_edn_inner(x[1], options, preserveSourceEntries);
|
|
388
404
|
}
|
|
389
405
|
if (x[0] === "quote") {
|
|
390
406
|
if (x.length !== 2) {
|
|
@@ -396,10 +412,10 @@ export let extract_cirru_edn = (x, options) => {
|
|
|
396
412
|
if (x.length < 2) {
|
|
397
413
|
throw new Error(`tuple expects at least 1 value, got: ${x}`);
|
|
398
414
|
}
|
|
399
|
-
return new CalcitTuple(
|
|
415
|
+
return new CalcitTuple(extract_cirru_edn_inner(x[1], options, preserveSourceEntries), x
|
|
400
416
|
.slice(2)
|
|
401
417
|
.filter(notComment)
|
|
402
|
-
.map((x) =>
|
|
418
|
+
.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries)));
|
|
403
419
|
}
|
|
404
420
|
if (x[0] === "%::") {
|
|
405
421
|
if (x.length < 3) {
|
|
@@ -413,21 +429,27 @@ export let extract_cirru_edn = (x, options) => {
|
|
|
413
429
|
// unwrap prototype to record then extract name
|
|
414
430
|
const proto = enumPrototype != null ? unwrap_enum_prototype_local(enumPrototype) : null;
|
|
415
431
|
const enumTag = proto != null ? proto.name.toString() : enumName;
|
|
416
|
-
return new CalcitTuple(
|
|
432
|
+
return new CalcitTuple(extract_cirru_edn_inner(x[2], options, preserveSourceEntries), x
|
|
417
433
|
.slice(3)
|
|
418
434
|
.filter(notComment)
|
|
419
|
-
.map((x) =>
|
|
435
|
+
.map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries)), enumPrototype);
|
|
420
436
|
}
|
|
421
437
|
if (x[0] === "atom") {
|
|
422
438
|
if (x.length !== 2) {
|
|
423
439
|
throw new Error(`atom expects 1 argument, got: ${x}`);
|
|
424
440
|
}
|
|
425
|
-
return atom(
|
|
441
|
+
return atom(extract_cirru_edn_inner(x[1], options, preserveSourceEntries));
|
|
426
442
|
}
|
|
427
443
|
}
|
|
428
444
|
console.error(x);
|
|
429
445
|
throw new Error(`Unexpected data from EDN: ${x}`);
|
|
430
446
|
};
|
|
447
|
+
export let extract_cirru_edn = (x, options) => {
|
|
448
|
+
return extract_cirru_edn_inner(x, options, false);
|
|
449
|
+
};
|
|
450
|
+
export let extract_cirru_edn_for_typed = (x, options) => {
|
|
451
|
+
return extract_cirru_edn_inner(x, options, true);
|
|
452
|
+
};
|
|
431
453
|
export let format_cirru_edn = (data, useInline = true) => {
|
|
432
454
|
if (data == null) {
|
|
433
455
|
return "\ndo nil" + "\n";
|
package/lib/js-enum.mjs
CHANGED
package/lib/js-impl.d.mts
CHANGED
|
@@ -8,6 +8,7 @@ export declare class CalcitImpl {
|
|
|
8
8
|
fields: Array<CalcitTag>;
|
|
9
9
|
values: Array<CalcitValue>;
|
|
10
10
|
cachedHash: Hash;
|
|
11
|
+
static [Symbol.hasInstance](value: unknown): boolean;
|
|
11
12
|
constructor(name: CalcitTag, fields: Array<CalcitTag>, values: Array<CalcitValue>, origin?: CalcitTrait | null);
|
|
12
13
|
get(k: CalcitValue): CalcitValue;
|
|
13
14
|
getOrNil(k: CalcitValue): CalcitValue;
|
package/lib/js-impl.mjs
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import { castTag, findInFields, toString } from "./calcit-data.mjs";
|
|
2
|
+
const CALCIT_IMPL_BRAND = Symbol.for("@calcit/procs/CalcitImpl");
|
|
2
3
|
export class CalcitImpl {
|
|
4
|
+
static [Symbol.hasInstance](value) {
|
|
5
|
+
if (typeof value !== "object" || value === null)
|
|
6
|
+
return false;
|
|
7
|
+
const candidate = value;
|
|
8
|
+
const brand = Object.getOwnPropertyDescriptor(candidate, CALCIT_IMPL_BRAND);
|
|
9
|
+
const name = Object.getOwnPropertyDescriptor(candidate, "name");
|
|
10
|
+
const origin = Object.getOwnPropertyDescriptor(candidate, "origin");
|
|
11
|
+
const fields = Object.getOwnPropertyDescriptor(candidate, "fields");
|
|
12
|
+
const values = Object.getOwnPropertyDescriptor(candidate, "values");
|
|
13
|
+
const cachedHash = Object.getOwnPropertyDescriptor(candidate, "cachedHash");
|
|
14
|
+
return (brand?.value === true &&
|
|
15
|
+
name !== undefined &&
|
|
16
|
+
origin !== undefined &&
|
|
17
|
+
fields !== undefined &&
|
|
18
|
+
values !== undefined &&
|
|
19
|
+
cachedHash !== undefined &&
|
|
20
|
+
typeof name.value === "object" &&
|
|
21
|
+
name.value !== null &&
|
|
22
|
+
typeof name.value.value === "string" &&
|
|
23
|
+
(origin.value === null || typeof origin.value === "object") &&
|
|
24
|
+
Array.isArray(fields.value) &&
|
|
25
|
+
Array.isArray(values.value));
|
|
26
|
+
}
|
|
3
27
|
constructor(name, fields, values, origin = null) {
|
|
28
|
+
// Vite can temporarily retain two copies of @calcit/procs while refreshing
|
|
29
|
+
// optimized dependencies. A global, non-enumerable brand keeps impl values
|
|
30
|
+
// recognizable across those otherwise distinct module instances.
|
|
31
|
+
Object.defineProperty(this, CALCIT_IMPL_BRAND, { value: true });
|
|
4
32
|
this.name = name;
|
|
5
33
|
this.origin = origin;
|
|
6
34
|
this.fields = fields;
|
package/lib/js-record.mjs
CHANGED
|
@@ -64,7 +64,7 @@ export class CalcitRecord {
|
|
|
64
64
|
}
|
|
65
65
|
toString(disableJsDataWarning = false) {
|
|
66
66
|
// Optimize string building using array join instead of concatenation
|
|
67
|
-
const parts = ["(%{} ", this.name.
|
|
67
|
+
const parts = ["(%{} '", this.name.value];
|
|
68
68
|
for (let idx = 0; idx < this.fields.length; idx++) {
|
|
69
69
|
parts.push(" (", this.fields[idx].toString(), " ", toString(this.values[idx], true, disableJsDataWarning), ")");
|
|
70
70
|
}
|
package/lib/js-struct.mjs
CHANGED
|
@@ -24,7 +24,7 @@ export class CalcitStruct {
|
|
|
24
24
|
if (this.fields.length !== this.fieldTypes.length) {
|
|
25
25
|
throw new Error("CalcitStruct: fields and fieldTypes length mismatch");
|
|
26
26
|
}
|
|
27
|
-
const parts = ["(%struct
|
|
27
|
+
const parts = ["(%struct '", this.name.value];
|
|
28
28
|
for (let idx = 0; idx < this.fields.length; idx++) {
|
|
29
29
|
const field = this.fields[idx];
|
|
30
30
|
const fieldType = this.fieldTypes[idx];
|
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calcit/procs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"main": "./lib/calcit.procs.mjs",
|
|
5
5
|
"devDependencies": {
|
|
6
6
|
"@types/node": "^25.7.0",
|
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
"test-rs": "cargo test -q",
|
|
17
17
|
"test-fail": "cargo test -q --bin cr type_fail_",
|
|
18
18
|
"test-snippets": "cargo test -q snippets::tests",
|
|
19
|
+
"check-js-runtime": "node scripts/check-js-runtime-identity.mjs",
|
|
19
20
|
"bench-recur-smoke": "cargo run --bin cr -- calcit/test.cirru js && node --input-type=module -e \"import { test_loop } from './js-out/test-recursion.main.mjs'; const n=3000; const t0=process.hrtime.bigint(); for(let i=0;i<n;i++) test_loop(); const dt=Number(process.hrtime.bigint()-t0)/1e6; console.log('test_loop_ms='+dt.toFixed(3));\"",
|
|
20
21
|
"check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
|
|
21
|
-
"check-all": "yarn compile && yarn check-agent-interface && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
|
|
22
|
+
"check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
|
|
22
23
|
"check-agent-interface": "cargo build --bin cr && node scripts/check-agent-interface.mjs",
|
|
23
24
|
"try-all": "yarn check-all",
|
|
24
25
|
"try-rs": "cargo run --bin cr -- calcit/test.cirru",
|