@calcit/procs 0.12.48 → 0.12.50
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/07-06-semantic-tree-navigation-rfc.md +569 -0
- package/RFCs/07-08-ffi-features-and-js-object-type-rfc.md +223 -0
- package/RFCs/07-19-doc-knowledge-index-rfc.md +195 -0
- package/RFCs/07-19-type-introspection-consistency-rfc.md +73 -0
- package/RFCs/README.md +3 -0
- package/build.rs +2 -4
- package/editing-history/2026-04-07-1550-path-format-at-prefix-migration.md +40 -0
- package/editing-history/2026-07-07-0000-semantic-tree-navigation.md +54 -0
- package/editing-history/2026-07-08-1650-features-and-js-object-type.md +27 -0
- package/editing-history/2026-07-09-1700-concise-method-representation.md +8 -0
- package/editing-history/2026-07-19-1919-type-introspection-consistency.md +24 -0
- package/editing-history/2026-07-19-1930-schema-tag-roundtrip-fix.md +64 -0
- package/editing-history/20260719-2236-doc-knowledge-graph.md +27 -0
- package/editing-history/20260719-2245-doc-graph-missing-filter.md +17 -0
- package/editing-history/20260719-2310-doc-graph-explain-full.md +16 -0
- package/lib/calcit-data.mjs +20 -1
- package/lib/calcit.procs.mjs +37 -7
- package/lib/package.json +1 -1
- package/package.json +1 -1
- package/ts-src/calcit-data.mts +20 -1
- package/ts-src/calcit.procs.mts +35 -7
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Edit History - Type Introspection Consistency Fixes
|
|
2
|
+
|
|
3
|
+
- **Objective**: Close gaps found while researching "how to discover a type's fields/methods in Calcit" (methods via traits/impls, fields via records, Display formatting). Documented findings and a prioritized fix plan in `RFCs/07-19-type-introspection-consistency-rfc.md`, then implemented items 1-3 (item 4 deferred as optional/out of scope for this pass).
|
|
4
|
+
|
|
5
|
+
- **Fix 1 — `Enum` `Display` now includes variants** (`src/calcit.rs`):
|
|
6
|
+
- Previously `Calcit::Enum` printed only `(%enum :Name)`, discarding the already-tracked `variants()` (tag + payload types). Now prints `(%enum :Name (:variant1 type ...) (:variant2 ...) ...)`, matching the richness of `Struct`'s Display.
|
|
7
|
+
- Test: `enum_display_includes_variants` in `src/calcit.rs`'s test module.
|
|
8
|
+
|
|
9
|
+
- **Fix 2 — `&methods-of`/`&inspect-methods` accept bare `Struct`/`Enum`/`Trait` values** (not just instances):
|
|
10
|
+
- `src/builtins/meta.rs`: `collect_impl_records_for_value` gained `Calcit::Struct`/`Calcit::Enum` arms (read their own `impls` field directly). `iter_impls_in_precedence_order` now treats `Struct`/`Enum` the same as `Tuple`/`Record` (last-attached impl wins, i.e. reversed iteration) since `&struct:impl-traits`/`&enum:impl-traits` also `.extend()` impls onto the end.
|
|
11
|
+
- `Calcit::Trait` handled as a special case (new helper `trait_dot_method_names`) since traits declare methods directly, not via an `impls` list — no `impls` field exists on `CalcitTrait`.
|
|
12
|
+
- JS target parity: `ts-src/calcit.procs.mts`'s `lookup_impls` gained `CalcitStruct`/`CalcitEnum` branches; the `reverse`-order flag (previously only `CalcitRecord || CalcitTuple`) now also includes `CalcitStruct || CalcitEnum`; `_$n_methods_of`/`_$n_inspect_methods` special-case `CalcitTrait` the same way as the Rust side.
|
|
13
|
+
- Test: `calcit/test-traits.cirru`'s `test-method-introspection` — new `let` block calling `&methods-of` on `(impl-traits Person0 MyFooImpl)` (bare struct), `DemoBar` (bare enum), `MyFoo` (bare trait), asserting the expected method tags show up. Edited via `cr tree insert-after` (module entry must be the module's own file, e.g. `cr calcit/test-traits.cirru ...`, NOT the aggregated `calcit/test.cirru` — `cr tree`/`cr edit` couldn't resolve the namespace when invoked through the aggregator, while `cr query` could).
|
|
14
|
+
|
|
15
|
+
- **Fix 3 — `to-pairs`/`keys` type signature no longer false-warns on records**:
|
|
16
|
+
- `src/calcit/type_annotation.rs`: `matches_with_bindings`'s `(TypeRef, Record)` arm previously only matched if the `TypeRef` name equaled the record's own struct name (e.g. `"map"` vs `"Person"` → false). Added: when the `TypeRef` name is the generic `"map"` placeholder, match any record structurally (records are field-name → value, i.e. map-like), in addition to the existing exact-name match.
|
|
17
|
+
- Test: `generic_map_type_ref_accepts_records_structurally` in `src/calcit/type_annotation.rs`'s test module — verifies `TypeRef("map")` matches `Record(Person)` both directions, and confirms unrelated `TypeRef` names still don't match structurally (no over-broadening).
|
|
18
|
+
- Note: empirically, `cr --check-only` on `test-record.cirru` didn't show a warning for `keys p2` even before this fix (likely because that call site's static type inference doesn't currently narrow `p2` to `Record`), so this is a defensive/forward-looking fix for when static inference improves or explicit type annotations are used, not a fix for an observed regression today.
|
|
19
|
+
|
|
20
|
+
- **Deferred (RFC item 4, optional)**: `&struct:fields`/`&enum:variants` new introspection procs — would require registering a new `CalcitProc` end-to-end (proc_name.rs signature + string mapping, builtins.rs dispatch, records.rs/meta.rs impl, JS/IR/WASM codegen parity). Judged lower priority/cost-to-benefit than items 1-3 which fixed real capability gaps; left as a follow-up.
|
|
21
|
+
|
|
22
|
+
- **Tooling note**: `cr tree`/`cr edit` subcommands need the entry file to be the *specific module's own `.cirru` file* (e.g. `calcit/test-traits.cirru`) to resolve `<ns>/<def>` targets — passing the aggregator entry (`calcit/test.cirru`, which just lists the module in `:modules`) causes `Error: "Namespace '...' not found"` for `tree`/`edit` even though `cr query defs/def` works fine through the aggregator. `--file` snippet content must be `quote`-prefixed (e.g. `quote (let (...) ...)`) even though the repo's `Agents.md` `--stdin` removal note doesn't mention this requirement explicitly.
|
|
23
|
+
|
|
24
|
+
- **Validation**: `cargo fmt`, `cargo clippy -- -D warnings`, `cargo test` (239 lib tests incl. 2 new), `cargo run --bin cr -- calcit/test.cirru`, `yarn check-all` (compile + try-rs + try-js + try-ir + try-wasm) — all clean/passing.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Schema kind tag round-trip regression fix (2026-07-19 19:30)
|
|
2
|
+
|
|
3
|
+
## Symptom
|
|
4
|
+
|
|
5
|
+
Running `cr tree insert-after`/`cr edit` on [calcit/test-traits.cirru](../calcit/test-traits.cirru)
|
|
6
|
+
(to add new `test-method-introspection` assertions) silently rewrote the `:schema`
|
|
7
|
+
field of *every other, unrelated* def in the same file from its specific kind tag
|
|
8
|
+
(`:enum`, `:trait`, `:impl`, `:struct`) down to `:schema :dynamic`. This affected
|
|
9
|
+
~15 defs (`Demo0`, `DemoBar`, `MyBar`, `MyBarImpl`, `MyFoo`, `MyFooImpl`, `Person0`,
|
|
10
|
+
etc.) that were never touched by the edit itself — any save of the file degraded
|
|
11
|
+
their schema fidelity as a side effect.
|
|
12
|
+
|
|
13
|
+
## Root cause
|
|
14
|
+
|
|
15
|
+
`:struct`/`:enum`/`:trait`/`:impl`/`:record` shorthand schema tags load into
|
|
16
|
+
`CalcitTypeAnnotation::Custom(Arc<Calcit>)` (see `CalcitTypeAnnotation::from_tag_name`
|
|
17
|
+
in [src/calcit/type_annotation.rs](../src/calcit/type_annotation.rs)), wrapping the
|
|
18
|
+
kind as a bare `Calcit::Tag`. On save, `schema_annotation_to_edn` (in
|
|
19
|
+
[src/snapshot.rs](../src/snapshot.rs)) converted the in-memory schema back to EDN via
|
|
20
|
+
`CalcitTypeAnnotation::builtin_tag_name()`, which only handles primitive scalar
|
|
21
|
+
types (`bool`, `number`, `string`, ...) and has no arm for `Custom`, `Record`,
|
|
22
|
+
`Struct`, `Enum`, or `Trait` — those all fell through the `_ => None` catch-all,
|
|
23
|
+
so `schema_annotation_to_edn` silently defaulted to `Edn::tag("dynamic")`.
|
|
24
|
+
|
|
25
|
+
Since `code_entry_edn_pairs` recomputes every entry's schema EDN from its in-memory
|
|
26
|
+
`CalcitTypeAnnotation` on *every* file save (not just for touched defs), any
|
|
27
|
+
`cr edit`/`cr tree` write to a file containing these shorthand-tagged schemas
|
|
28
|
+
degrades them all to `:dynamic`, regardless of which def was actually edited.
|
|
29
|
+
|
|
30
|
+
Note `builtin_tag_name()` is also used by `to_brief_string()` and other call
|
|
31
|
+
sites that intentionally want more specific output (e.g. `struct Person` instead
|
|
32
|
+
of a bare `:struct`) for those variants — so widening `builtin_tag_name()` itself
|
|
33
|
+
to cover them would have regressed those call sites. The fix instead lives
|
|
34
|
+
directly in `schema_annotation_to_edn`, which is the one place that needs the
|
|
35
|
+
short kind-tag representation for the snapshot file format.
|
|
36
|
+
|
|
37
|
+
## Fix
|
|
38
|
+
|
|
39
|
+
Added explicit match arms to `schema_annotation_to_edn` in [src/snapshot.rs](../src/snapshot.rs):
|
|
40
|
+
- `Custom(value)`: coerce the wrapped `Calcit::Tag` back into its `Edn::Tag` (falls
|
|
41
|
+
back to `:dynamic` only if the wrapped value isn't a bare tag, which shouldn't
|
|
42
|
+
happen via `from_tag_name`).
|
|
43
|
+
- `Record(_)` → `:record`, `Struct(..)` → `:struct`, `Enum(..)` → `:enum`,
|
|
44
|
+
`Trait(_)` → `:trait`.
|
|
45
|
+
|
|
46
|
+
This preserves the user's original expressed intent (e.g. `:schema :impl`) instead
|
|
47
|
+
of silently downgrading it to `:dynamic`, which would otherwise turn off
|
|
48
|
+
schema-based type-checking coverage for those defs.
|
|
49
|
+
|
|
50
|
+
Recovered the previously-corrupted `calcit/test-traits.cirru` by reverting to
|
|
51
|
+
`git checkout` and reapplying the new `test-method-introspection` assertions with
|
|
52
|
+
the fixed binary — confirmed via `git diff` that this time only the intended
|
|
53
|
+
7 new lines changed, with zero incidental `:schema` churn.
|
|
54
|
+
|
|
55
|
+
## Regression test
|
|
56
|
+
|
|
57
|
+
`test_custom_kind_schema_tags_round_trip_instead_of_degrading_to_dynamic` in
|
|
58
|
+
[src/snapshot.rs](../src/snapshot.rs) asserts `schema_annotation_to_edn(CalcitTypeAnnotation::from_tag_name(kind))`
|
|
59
|
+
round-trips to `Edn::tag(kind)` for `struct`/`enum`/`trait`/`impl`/`record`.
|
|
60
|
+
|
|
61
|
+
## Validation
|
|
62
|
+
|
|
63
|
+
`cargo fmt`, `cargo clippy --all-targets -- -D warnings`, `cargo test` (all pass),
|
|
64
|
+
`yarn check-all` (exit 0).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 2026-07-19 22:36 文档知识图谱与增量缓存
|
|
2
|
+
|
|
3
|
+
## 修改概要
|
|
4
|
+
|
|
5
|
+
- 为 `cr docs` 增加文档知识图构建、检查、关系遍历、路径查询、定义反查、缺失定义和孤立节点命令。
|
|
6
|
+
- 使用 Markdown frontmatter 的 `id`、`parent`、`related`、`requires`、`leads_to`、`code_refs` 表达可 Git 管理的知识关系。
|
|
7
|
+
- 在 `~/.config/calcit/docs-cache/` 生成 JSON 文本缓存,按文件内容、解析器/schema 版本和内置 Calcit snapshot 指纹失效。
|
|
8
|
+
- 为文档索引 RFC、`CalcitAgent.md`、索引规范和验证案例补充使用说明。
|
|
9
|
+
- 为核心数据结构文档补充稳定节点 ID 与 Calcit 定义引用,验证从概念到 API 和编辑工作流的跳转。
|
|
10
|
+
|
|
11
|
+
## 验证
|
|
12
|
+
|
|
13
|
+
- `cargo fmt --all`
|
|
14
|
+
- `cargo clippy --all-targets -- -D warnings`
|
|
15
|
+
- `cargo test -q`
|
|
16
|
+
- `yarn compile`
|
|
17
|
+
- `cr docs graph build`
|
|
18
|
+
- `cr docs graph check`
|
|
19
|
+
- `cr docs graph path core/features/list core/run/edit-tree`
|
|
20
|
+
- `cr docs graph explain calcit.core/nth`
|
|
21
|
+
- `cr docs graph missing`
|
|
22
|
+
- `cr docs graph orphans`
|
|
23
|
+
|
|
24
|
+
## 后续方向
|
|
25
|
+
|
|
26
|
+
- 将 `missing` 从总量报告扩展为按公共 API、语法符号和内部定义分类。
|
|
27
|
+
- 接入模块或应用级 Calcit snapshot,并增加示例覆盖检查与 LLM context 聚合。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 2026-07-19 22:45 graph missing 分批筛选
|
|
2
|
+
|
|
3
|
+
## 修改概要
|
|
4
|
+
|
|
5
|
+
- 为 `cr docs graph missing` 增加 `--ns` namespace 前缀过滤。
|
|
6
|
+
- 增加 `--limit` 输出数量限制,便于按批次补充公共 API 的 `code_refs`。
|
|
7
|
+
- 在 `CalcitAgent.md` 和知识图谱 RFC 中补充该操作说明。
|
|
8
|
+
- 增加 namespace 过滤、数量限制和内部命名排除的单元测试。
|
|
9
|
+
|
|
10
|
+
## 验证
|
|
11
|
+
|
|
12
|
+
- `cargo fmt --all`
|
|
13
|
+
- `cargo test -q`
|
|
14
|
+
- `cargo clippy --all-targets -- -D warnings`
|
|
15
|
+
- `yarn compile`
|
|
16
|
+
- `cr docs graph missing --ns calcit.core --limit 5`
|
|
17
|
+
- `git diff --check`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# 2026-07-19 23:10 graph explain 定义摘要
|
|
2
|
+
|
|
3
|
+
## 修改概要
|
|
4
|
+
|
|
5
|
+
- 为 `cr docs graph explain` 增加 `--full`,输出 Calcit 定义的 doc 和 examples 可用性。
|
|
6
|
+
- 缓存定义记录保存 doc 文本,并将 cache schema 升级为 5,确保旧缓存自动重建。
|
|
7
|
+
- 更新 command echo、`CalcitAgent.md` 和 graph 验证案例。
|
|
8
|
+
|
|
9
|
+
## 验证
|
|
10
|
+
|
|
11
|
+
- `cargo fmt --all`
|
|
12
|
+
- `cargo test -q`
|
|
13
|
+
- `cargo clippy --all-targets -- -D warnings`
|
|
14
|
+
- `yarn compile`
|
|
15
|
+
- `cr docs graph explain calcit.core/nth --full`
|
|
16
|
+
- `git diff --check`
|
package/lib/calcit-data.mjs
CHANGED
|
@@ -210,6 +210,14 @@ export let hashFunction = (x) => {
|
|
|
210
210
|
return h;
|
|
211
211
|
}
|
|
212
212
|
if (typeof x === "function") {
|
|
213
|
+
// method values are closures created on the fly (see invoke_method_closure);
|
|
214
|
+
// hash by method name so equal methods share the same hash, matching isEqual
|
|
215
|
+
const methodName = x.__calcitMethodName;
|
|
216
|
+
if (methodName != null) {
|
|
217
|
+
let h = mergeValueHash(defaultHash_fn, methodName);
|
|
218
|
+
x[calcit_dirty_hash_key] = h;
|
|
219
|
+
return h;
|
|
220
|
+
}
|
|
213
221
|
fnHashCounter = fnHashCounter + 1;
|
|
214
222
|
let h = mergeValueHash(defaultHash_fn, fnHashCounter);
|
|
215
223
|
x[calcit_dirty_hash_key] = h;
|
|
@@ -383,6 +391,10 @@ export let toString = (x, escaped, disableJsDataWarning = false) => {
|
|
|
383
391
|
return x.toString();
|
|
384
392
|
}
|
|
385
393
|
if (typeof x === "function") {
|
|
394
|
+
const methodName = x.__calcitMethodName;
|
|
395
|
+
if (methodName != null) {
|
|
396
|
+
return "." + methodName;
|
|
397
|
+
}
|
|
386
398
|
return `(&fn ...)`;
|
|
387
399
|
}
|
|
388
400
|
if (x instanceof CalcitSymbol) {
|
|
@@ -541,7 +553,14 @@ export let _$n__$e_ = (x, y) => {
|
|
|
541
553
|
return false;
|
|
542
554
|
}
|
|
543
555
|
if (tx === "function") {
|
|
544
|
-
//
|
|
556
|
+
// method values are closures created on the fly (see invoke_method_closure),
|
|
557
|
+
// so two methods with the same name must compare equal by name, not by reference
|
|
558
|
+
const mx = x.__calcitMethodName;
|
|
559
|
+
const my = y.__calcitMethodName;
|
|
560
|
+
if (mx != null || my != null) {
|
|
561
|
+
return mx === my;
|
|
562
|
+
}
|
|
563
|
+
// comparing plain functions by reference
|
|
545
564
|
return x === y;
|
|
546
565
|
}
|
|
547
566
|
if (x instanceof CalcitTag) {
|
package/lib/calcit.procs.mjs
CHANGED
|
@@ -1834,6 +1834,17 @@ function lookup_impls(obj) {
|
|
|
1834
1834
|
tag = "&core-set-methods";
|
|
1835
1835
|
impls = normalize_builtin_impls(calcit_builtin_impls.set);
|
|
1836
1836
|
}
|
|
1837
|
+
else if (obj instanceof CalcitStruct) {
|
|
1838
|
+
// Bare type definitions (not yet instantiated) carry their own attached
|
|
1839
|
+
// impls, so introspection tools like `&methods-of` can answer "what
|
|
1840
|
+
// methods will instances of this type have" without a concrete instance.
|
|
1841
|
+
tag = obj.name.toString();
|
|
1842
|
+
impls = obj.impls;
|
|
1843
|
+
}
|
|
1844
|
+
else if (obj instanceof CalcitEnum) {
|
|
1845
|
+
tag = obj.name();
|
|
1846
|
+
impls = obj.impls;
|
|
1847
|
+
}
|
|
1837
1848
|
else if (typeof obj === "number") {
|
|
1838
1849
|
tag = "&core-number-methods";
|
|
1839
1850
|
impls = normalize_builtin_impls(calcit_builtin_impls.number);
|
|
@@ -1862,7 +1873,7 @@ export function invoke_method(p, obj, ...args) {
|
|
|
1862
1873
|
let tag = pair[1];
|
|
1863
1874
|
// builtin impl lists are ordered by priority in calcit-core.
|
|
1864
1875
|
// user-defined values use impl-traits append, so later impls override earlier ones.
|
|
1865
|
-
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple;
|
|
1876
|
+
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple || obj instanceof CalcitStruct || obj instanceof CalcitEnum;
|
|
1866
1877
|
let idx = reverse ? impls.length - 1 : 0;
|
|
1867
1878
|
while (reverse ? idx >= 0 : idx < impls.length) {
|
|
1868
1879
|
let klass = impls[idx];
|
|
@@ -1882,12 +1893,16 @@ export function invoke_method(p, obj, ...args) {
|
|
|
1882
1893
|
export function _$n_methods_of(obj) {
|
|
1883
1894
|
if (arguments.length !== 1)
|
|
1884
1895
|
throw new Error("&methods-of expected 1 argument");
|
|
1896
|
+
// Traits declare methods directly rather than through attached impls.
|
|
1897
|
+
if (obj instanceof CalcitTrait) {
|
|
1898
|
+
return new CalcitSliceList(obj.methods.map((m) => invoke_method_closure(m.value)));
|
|
1899
|
+
}
|
|
1885
1900
|
let pair = lookup_impls(obj);
|
|
1886
1901
|
if (pair == null) {
|
|
1887
1902
|
throw new Error(`&methods-of cannot resolve impls for: ${toString(obj, true)}`);
|
|
1888
1903
|
}
|
|
1889
1904
|
let impls = pair[0];
|
|
1890
|
-
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple;
|
|
1905
|
+
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple || obj instanceof CalcitStruct || obj instanceof CalcitEnum;
|
|
1891
1906
|
let seen = new Set();
|
|
1892
1907
|
let ys = [];
|
|
1893
1908
|
let idx = reverse ? impls.length - 1 : 0;
|
|
@@ -1895,10 +1910,11 @@ export function _$n_methods_of(obj) {
|
|
|
1895
1910
|
let impl = impls[idx];
|
|
1896
1911
|
if (impl != null) {
|
|
1897
1912
|
for (let k = 0; k < impl.fields.length; k++) {
|
|
1898
|
-
let
|
|
1913
|
+
let rawName = impl.fields[k].value;
|
|
1914
|
+
let name = "." + rawName;
|
|
1899
1915
|
if (!seen.has(name)) {
|
|
1900
1916
|
seen.add(name);
|
|
1901
|
-
ys.push(
|
|
1917
|
+
ys.push(invoke_method_closure(rawName));
|
|
1902
1918
|
}
|
|
1903
1919
|
}
|
|
1904
1920
|
}
|
|
@@ -1909,12 +1925,26 @@ export function _$n_methods_of(obj) {
|
|
|
1909
1925
|
export function _$n_inspect_methods(obj, note) {
|
|
1910
1926
|
if (arguments.length !== 2)
|
|
1911
1927
|
throw new Error("&inspect-methods expected 2 arguments");
|
|
1928
|
+
if (obj instanceof CalcitTrait) {
|
|
1929
|
+
console.log("\n&inspect-methods");
|
|
1930
|
+
console.log(`Note: ${toString(note, true)}`);
|
|
1931
|
+
console.log(`Value type: ${type_of(obj).toString()}`);
|
|
1932
|
+
console.log(`Value: ${toString(obj, true)}`);
|
|
1933
|
+
console.log("Method call syntax: `.method self p1 p2`");
|
|
1934
|
+
console.log(" - dot is part of the method name, first arg is the receiver\n");
|
|
1935
|
+
const names = obj.methods.map((m) => "." + m.value);
|
|
1936
|
+
console.log(`Trait methods declared directly (no impls): ${names.length}`);
|
|
1937
|
+
console.log(`\nAll methods (unique, high → low): ${names.length}`);
|
|
1938
|
+
console.log(" " + names.join(" "));
|
|
1939
|
+
console.log("\n");
|
|
1940
|
+
return obj;
|
|
1941
|
+
}
|
|
1912
1942
|
let pair = lookup_impls(obj);
|
|
1913
1943
|
if (pair == null) {
|
|
1914
1944
|
throw new Error(`&inspect-methods cannot resolve impls for: ${toString(obj, true)}`);
|
|
1915
1945
|
}
|
|
1916
1946
|
let impls = pair[0];
|
|
1917
|
-
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple;
|
|
1947
|
+
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple || obj instanceof CalcitStruct || obj instanceof CalcitEnum;
|
|
1918
1948
|
console.log("\n&inspect-methods");
|
|
1919
1949
|
console.log(`Note: ${toString(note, true)}`);
|
|
1920
1950
|
console.log(`Value type: ${type_of(obj).toString()}`);
|
|
@@ -1941,7 +1971,7 @@ export function _$n_inspect_methods(obj, note) {
|
|
|
1941
1971
|
}
|
|
1942
1972
|
let ms = _$n_methods_of(obj);
|
|
1943
1973
|
console.log(`\nAll methods (unique, high → low): ${ms.len()}`);
|
|
1944
|
-
console.log(" " + Array.from(ms.items()).join(" "));
|
|
1974
|
+
console.log(" " + Array.from(ms.items()).map((m) => toString(m, false)).join(" "));
|
|
1945
1975
|
console.log("\n");
|
|
1946
1976
|
return obj;
|
|
1947
1977
|
}
|
|
@@ -1963,7 +1993,7 @@ export function _$n_trait_call(traitDef, method, obj, ...args) {
|
|
|
1963
1993
|
throw new Error(`&trait-call cannot resolve impls for: ${toString(obj, true)}`);
|
|
1964
1994
|
}
|
|
1965
1995
|
const impls = pair[0];
|
|
1966
|
-
const reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple;
|
|
1996
|
+
const reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple || obj instanceof CalcitStruct || obj instanceof CalcitEnum;
|
|
1967
1997
|
let idx = reverse ? impls.length - 1 : 0;
|
|
1968
1998
|
while (reverse ? idx >= 0 : idx < impls.length) {
|
|
1969
1999
|
const impl = impls[idx];
|
package/lib/package.json
CHANGED
package/package.json
CHANGED
package/ts-src/calcit-data.mts
CHANGED
|
@@ -238,6 +238,14 @@ export let hashFunction = (x: CalcitValue): Hash => {
|
|
|
238
238
|
return h;
|
|
239
239
|
}
|
|
240
240
|
if (typeof x === "function") {
|
|
241
|
+
// method values are closures created on the fly (see invoke_method_closure);
|
|
242
|
+
// hash by method name so equal methods share the same hash, matching isEqual
|
|
243
|
+
const methodName = (x as { __calcitMethodName?: string }).__calcitMethodName;
|
|
244
|
+
if (methodName != null) {
|
|
245
|
+
let h = mergeValueHash(defaultHash_fn, methodName);
|
|
246
|
+
(x as any)[calcit_dirty_hash_key] = h;
|
|
247
|
+
return h;
|
|
248
|
+
}
|
|
241
249
|
fnHashCounter = fnHashCounter + 1;
|
|
242
250
|
let h = mergeValueHash(defaultHash_fn, fnHashCounter);
|
|
243
251
|
(x as any)[calcit_dirty_hash_key] = h;
|
|
@@ -412,6 +420,10 @@ export let toString = (x: CalcitValue, escaped: boolean, disableJsDataWarning: b
|
|
|
412
420
|
return x.toString();
|
|
413
421
|
}
|
|
414
422
|
if (typeof x === "function") {
|
|
423
|
+
const methodName = (x as { __calcitMethodName?: string }).__calcitMethodName;
|
|
424
|
+
if (methodName != null) {
|
|
425
|
+
return "." + methodName;
|
|
426
|
+
}
|
|
415
427
|
return `(&fn ...)`;
|
|
416
428
|
}
|
|
417
429
|
if (x instanceof CalcitSymbol) {
|
|
@@ -579,7 +591,14 @@ export let _$n__$e_ = (x: CalcitValue, y: CalcitValue): boolean => {
|
|
|
579
591
|
return false;
|
|
580
592
|
}
|
|
581
593
|
if (tx === "function") {
|
|
582
|
-
//
|
|
594
|
+
// method values are closures created on the fly (see invoke_method_closure),
|
|
595
|
+
// so two methods with the same name must compare equal by name, not by reference
|
|
596
|
+
const mx = (x as { __calcitMethodName?: string }).__calcitMethodName;
|
|
597
|
+
const my = (y as { __calcitMethodName?: string }).__calcitMethodName;
|
|
598
|
+
if (mx != null || my != null) {
|
|
599
|
+
return mx === my;
|
|
600
|
+
}
|
|
601
|
+
// comparing plain functions by reference
|
|
583
602
|
return x === y;
|
|
584
603
|
}
|
|
585
604
|
if (x instanceof CalcitTag) {
|
package/ts-src/calcit.procs.mts
CHANGED
|
@@ -1946,6 +1946,15 @@ function lookup_impls(obj: CalcitValue): [CalcitImpl[], string] {
|
|
|
1946
1946
|
} else if (obj instanceof CalcitSet) {
|
|
1947
1947
|
tag = "&core-set-methods";
|
|
1948
1948
|
impls = normalize_builtin_impls(calcit_builtin_impls.set);
|
|
1949
|
+
} else if (obj instanceof CalcitStruct) {
|
|
1950
|
+
// Bare type definitions (not yet instantiated) carry their own attached
|
|
1951
|
+
// impls, so introspection tools like `&methods-of` can answer "what
|
|
1952
|
+
// methods will instances of this type have" without a concrete instance.
|
|
1953
|
+
tag = obj.name.toString();
|
|
1954
|
+
impls = obj.impls;
|
|
1955
|
+
} else if (obj instanceof CalcitEnum) {
|
|
1956
|
+
tag = obj.name();
|
|
1957
|
+
impls = obj.impls;
|
|
1949
1958
|
} else if (typeof obj === "number") {
|
|
1950
1959
|
tag = "&core-number-methods";
|
|
1951
1960
|
impls = normalize_builtin_impls(calcit_builtin_impls.number);
|
|
@@ -1971,7 +1980,7 @@ export function invoke_method(p: string, obj: CalcitValue, ...args: CalcitValue[
|
|
|
1971
1980
|
let tag = pair[1];
|
|
1972
1981
|
// builtin impl lists are ordered by priority in calcit-core.
|
|
1973
1982
|
// user-defined values use impl-traits append, so later impls override earlier ones.
|
|
1974
|
-
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple;
|
|
1983
|
+
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple || obj instanceof CalcitStruct || obj instanceof CalcitEnum;
|
|
1975
1984
|
let idx = reverse ? impls.length - 1 : 0;
|
|
1976
1985
|
while (reverse ? idx >= 0 : idx < impls.length) {
|
|
1977
1986
|
let klass = impls[idx];
|
|
@@ -1991,12 +2000,16 @@ export function invoke_method(p: string, obj: CalcitValue, ...args: CalcitValue[
|
|
|
1991
2000
|
|
|
1992
2001
|
export function _$n_methods_of(obj: CalcitValue): CalcitSliceList {
|
|
1993
2002
|
if (arguments.length !== 1) throw new Error("&methods-of expected 1 argument");
|
|
2003
|
+
// Traits declare methods directly rather than through attached impls.
|
|
2004
|
+
if (obj instanceof CalcitTrait) {
|
|
2005
|
+
return new CalcitSliceList(obj.methods.map((m) => invoke_method_closure(m.value)));
|
|
2006
|
+
}
|
|
1994
2007
|
let pair = lookup_impls(obj);
|
|
1995
2008
|
if (pair == null) {
|
|
1996
2009
|
throw new Error(`&methods-of cannot resolve impls for: ${toString(obj, true)}`);
|
|
1997
2010
|
}
|
|
1998
2011
|
let impls = pair[0];
|
|
1999
|
-
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple;
|
|
2012
|
+
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple || obj instanceof CalcitStruct || obj instanceof CalcitEnum;
|
|
2000
2013
|
let seen = new Set<string>();
|
|
2001
2014
|
let ys: CalcitValue[] = [];
|
|
2002
2015
|
|
|
@@ -2005,10 +2018,11 @@ export function _$n_methods_of(obj: CalcitValue): CalcitSliceList {
|
|
|
2005
2018
|
let impl = impls[idx];
|
|
2006
2019
|
if (impl != null) {
|
|
2007
2020
|
for (let k = 0; k < impl.fields.length; k++) {
|
|
2008
|
-
let
|
|
2021
|
+
let rawName = impl.fields[k].value;
|
|
2022
|
+
let name = "." + rawName;
|
|
2009
2023
|
if (!seen.has(name)) {
|
|
2010
2024
|
seen.add(name);
|
|
2011
|
-
ys.push(
|
|
2025
|
+
ys.push(invoke_method_closure(rawName));
|
|
2012
2026
|
}
|
|
2013
2027
|
}
|
|
2014
2028
|
}
|
|
@@ -2019,12 +2033,26 @@ export function _$n_methods_of(obj: CalcitValue): CalcitSliceList {
|
|
|
2019
2033
|
|
|
2020
2034
|
export function _$n_inspect_methods(obj: CalcitValue, note: CalcitValue): CalcitValue {
|
|
2021
2035
|
if (arguments.length !== 2) throw new Error("&inspect-methods expected 2 arguments");
|
|
2036
|
+
if (obj instanceof CalcitTrait) {
|
|
2037
|
+
console.log("\n&inspect-methods");
|
|
2038
|
+
console.log(`Note: ${toString(note, true)}`);
|
|
2039
|
+
console.log(`Value type: ${type_of(obj).toString()}`);
|
|
2040
|
+
console.log(`Value: ${toString(obj, true)}`);
|
|
2041
|
+
console.log("Method call syntax: `.method self p1 p2`");
|
|
2042
|
+
console.log(" - dot is part of the method name, first arg is the receiver\n");
|
|
2043
|
+
const names = obj.methods.map((m) => "." + m.value);
|
|
2044
|
+
console.log(`Trait methods declared directly (no impls): ${names.length}`);
|
|
2045
|
+
console.log(`\nAll methods (unique, high → low): ${names.length}`);
|
|
2046
|
+
console.log(" " + names.join(" "));
|
|
2047
|
+
console.log("\n");
|
|
2048
|
+
return obj;
|
|
2049
|
+
}
|
|
2022
2050
|
let pair = lookup_impls(obj);
|
|
2023
2051
|
if (pair == null) {
|
|
2024
2052
|
throw new Error(`&inspect-methods cannot resolve impls for: ${toString(obj, true)}`);
|
|
2025
2053
|
}
|
|
2026
2054
|
let impls = pair[0];
|
|
2027
|
-
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple;
|
|
2055
|
+
let reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple || obj instanceof CalcitStruct || obj instanceof CalcitEnum;
|
|
2028
2056
|
|
|
2029
2057
|
console.log("\n&inspect-methods");
|
|
2030
2058
|
console.log(`Note: ${toString(note, true)}`);
|
|
@@ -2054,7 +2082,7 @@ export function _$n_inspect_methods(obj: CalcitValue, note: CalcitValue): Calcit
|
|
|
2054
2082
|
|
|
2055
2083
|
let ms = _$n_methods_of(obj);
|
|
2056
2084
|
console.log(`\nAll methods (unique, high → low): ${ms.len()}`);
|
|
2057
|
-
console.log(" " +
|
|
2085
|
+
console.log(" " + Array.from(ms.items()).map((m) => toString(m, false)).join(" "));
|
|
2058
2086
|
console.log("\n");
|
|
2059
2087
|
|
|
2060
2088
|
return obj;
|
|
@@ -2078,7 +2106,7 @@ export function _$n_trait_call(traitDef: CalcitValue, method: CalcitValue, obj:
|
|
|
2078
2106
|
throw new Error(`&trait-call cannot resolve impls for: ${toString(obj, true)}`);
|
|
2079
2107
|
}
|
|
2080
2108
|
const impls = pair[0];
|
|
2081
|
-
const reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple;
|
|
2109
|
+
const reverse = obj instanceof CalcitRecord || obj instanceof CalcitTuple || obj instanceof CalcitStruct || obj instanceof CalcitEnum;
|
|
2082
2110
|
let idx = reverse ? impls.length - 1 : 0;
|
|
2083
2111
|
while (reverse ? idx >= 0 : idx < impls.length) {
|
|
2084
2112
|
const impl = impls[idx];
|