@calcit/procs 0.11.0-a9 → 0.11.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.
Files changed (34) hide show
  1. package/.gitattributes +1 -0
  2. package/.yarn/install-state.gz +0 -0
  3. package/build.rs +74 -0
  4. package/editing-history/2026-0213-1841-trait-origin-structural-eq.md +91 -0
  5. package/editing-history/2026-0213-1926-docs-tests-followup.md +33 -0
  6. package/editing-history/2026-0214-2050-js-codegen-recursion-tag-migration.md +39 -0
  7. package/editing-history/2026-0214-2358-emit-js-modularization-shift-left.md +63 -0
  8. package/editing-history/2026-0215-1941-macro-diagnostics-refinement.md +39 -0
  9. package/editing-history/2026-0215-diagnostics-consolidated.md +35 -0
  10. package/editing-history/2026-0216-2043-core-api-docs-cleanup.md +40 -0
  11. package/editing-history/2026-0216-2128-docs-smoke-and-trait-errors.md +20 -0
  12. package/lib/calcit-data.mjs +9 -0
  13. package/lib/calcit.procs.mjs +60 -21
  14. package/lib/custom-formatter.mjs +2 -2
  15. package/lib/js-arity-helpers.mjs +9 -0
  16. package/lib/js-cirru.mjs +3 -3
  17. package/lib/js-enum.mjs +11 -5
  18. package/lib/js-impl.mjs +2 -1
  19. package/lib/js-record.mjs +32 -16
  20. package/lib/js-tag-helpers.mjs +15 -0
  21. package/lib/js-tuple.mjs +12 -10
  22. package/lib/package.json +9 -2
  23. package/package.json +9 -2
  24. package/ts-src/calcit-data.mts +9 -0
  25. package/ts-src/calcit.procs.mts +63 -23
  26. package/ts-src/custom-formatter.mts +2 -2
  27. package/ts-src/js-arity-helpers.mts +11 -0
  28. package/ts-src/js-cirru.mts +2 -4
  29. package/ts-src/js-enum.mts +11 -6
  30. package/ts-src/js-impl.mts +4 -1
  31. package/ts-src/js-primes.mts +2 -0
  32. package/ts-src/js-record.mts +32 -17
  33. package/ts-src/js-tag-helpers.mts +17 -0
  34. package/ts-src/js-tuple.mts +14 -11
package/.gitattributes CHANGED
@@ -3,3 +3,4 @@ calcit.cirru -diff linguist-generated
3
3
  yarn.lock -diff linguist-generated
4
4
  Cargo.lock -diff linguist-generated
5
5
  lib -diff linguist-generated
6
+ editing-history -diff linguist-generated
Binary file
package/build.rs ADDED
@@ -0,0 +1,74 @@
1
+ use cirru_edn::{Edn, from_edn};
2
+ use cirru_parser::Cirru;
3
+ use serde::{Deserialize, Serialize};
4
+ use std::collections::HashMap;
5
+ use std::env;
6
+ use std::fs;
7
+ use std::path::Path;
8
+
9
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10
+ pub struct SnapshotConfigs {
11
+ #[serde(rename = "init-fn")]
12
+ pub init_fn: String,
13
+ #[serde(rename = "reload-fn")]
14
+ pub reload_fn: String,
15
+ #[serde(default)]
16
+ pub modules: Vec<String>,
17
+ #[serde(default)]
18
+ pub version: String,
19
+ }
20
+
21
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22
+ pub struct CodeEntry {
23
+ pub doc: String,
24
+ #[serde(default)]
25
+ pub examples: Vec<Cirru>,
26
+ pub code: Cirru,
27
+ }
28
+
29
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30
+ pub struct FileInSnapShot {
31
+ pub ns: CodeEntry,
32
+ pub defs: HashMap<String, CodeEntry>,
33
+ }
34
+
35
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36
+ pub struct Snapshot {
37
+ pub package: String,
38
+ pub about: Option<String>,
39
+ pub configs: SnapshotConfigs,
40
+ pub entries: HashMap<String, SnapshotConfigs>,
41
+ pub files: HashMap<String, FileInSnapShot>,
42
+ }
43
+
44
+ fn main() {
45
+ println!("cargo:rerun-if-changed=src/cirru/calcit-core.cirru");
46
+
47
+ let out_dir = env::var_os("OUT_DIR").unwrap();
48
+ let dest_path = Path::new(&out_dir).join("calcit-core.rmp");
49
+
50
+ let core_content = fs::read_to_string("src/cirru/calcit-core.cirru").expect("read core");
51
+ let core_data = cirru_edn::parse(&core_content).expect("parse core");
52
+
53
+ // Minimal logic to convert Edn to Snapshot as in src/snapshot.rs
54
+ let data = core_data.view_map().expect("map");
55
+ let pkg: String = from_edn(data.get_or_nil("package")).expect("pkg");
56
+ let about = match data.get_or_nil("about") {
57
+ Edn::Nil => None,
58
+ value => Some(from_edn::<String>(value).expect("about")),
59
+ };
60
+
61
+ let files: HashMap<String, FileInSnapShot> = from_edn(data.get_or_nil("files")).expect("files");
62
+
63
+ let snapshot = Snapshot {
64
+ package: pkg,
65
+ about,
66
+ configs: from_edn(data.get_or_nil("configs")).expect("configs"),
67
+ entries: from_edn(data.get_or_nil("entries")).expect("entries"),
68
+ files,
69
+ };
70
+
71
+ let mut buf = Vec::new();
72
+ snapshot.serialize(&mut rmp_serde::Serializer::new(&mut buf)).expect("serialize");
73
+ fs::write(dest_path, buf).expect("write");
74
+ }
@@ -0,0 +1,91 @@
1
+ # 202602131841 trait origin 匹配与结构化 trait 相等性
2
+
3
+ ## 背景
4
+
5
+ 本次提交延续了动态方法告警收敛与 trait 分发可靠性改进。
6
+ 评审中提出的核心问题是:trait 分发不应只依赖 trait 名称匹配。
7
+ `impl` 记录本身已经携带了 trait 的 origin 元数据,因此匹配应直接基于 origin。
8
+
9
+ ## 目标
10
+
11
+ 1. 让 `&trait-call` 按 trait origin 相等性选择 impl,而不是按名称字符串匹配。
12
+ 2. 让 trait/impl 的相等语义与上述运行时选择模型一致。
13
+ 3. 在尽量保持兼容的前提下,提高歧义/重名场景下的正确性。
14
+ 4. 保持现有优先级规则不变(用户 impl:后者覆盖前者;内建 impl:前者优先)。
15
+
16
+ ## 思考过程
17
+
18
+ ### 为什么 name-only 匹配不够
19
+
20
+ - 仅按名称匹配会把“同名但结构不同”的 trait 混为一类。
21
+ - 运行时已有 `CalcitImpl.origin: Option<Arc<CalcitTrait>>`,若忽略它会浪费已有语义信息。
22
+ - 使用显式 `&trait-call` 时,调用方预期的是“精确 trait 命中”,而非按名字近似匹配。
23
+
24
+ ### 分发策略决定
25
+
26
+ 在 `trait_call` 中:
27
+
28
+ - 旧逻辑:`imp.trait_name()` 与 `trait_def.name` 比较。
29
+ - 新逻辑:`imp.origin()` 与目标 trait 直接通过 `Eq` 比较。
30
+
31
+ 该方案改动小、语义清晰,并与当前运行时模型一致,不引入额外解析复杂度。
32
+
33
+ ### Eq/Hash 一致性
34
+
35
+ 当分发切换到 origin 相等后,`CalcitTrait` 的相等语义就不能继续只看 `name`。
36
+
37
+ 因此将 `CalcitTrait` 的 Eq/Hash 升级为结构化比较,包含:
38
+
39
+ - trait 名称
40
+ - 方法名列表
41
+ - 方法类型签名
42
+ - requires(依赖 trait)
43
+ - 默认实现槽位形状(每个位置是 `Some` 还是 `None`)
44
+
45
+ 关于默认实现:
46
+
47
+ - 默认函数体(`CalcitFn`)不做深比较/深哈希,因为 `CalcitFn` 当前不提供完整 Eq/Hash。
48
+ - 这里只比较默认槽位可用性形状,以保证 Eq/Hash 契约一致,同时提升结构判断准确性。
49
+
50
+ ## 文件级改动
51
+
52
+ ### 运行时分发与文档
53
+
54
+ - `src/builtins/meta.rs`
55
+ - `trait_call` 改为按 `impl.origin` 相等匹配。
56
+ - 同步更新函数附近注释,确保文档与实现一致。
57
+
58
+ ### impl 相等性
59
+
60
+ - `src/calcit/calcit_impl.rs`
61
+ - `PartialEq` 改为直接比较 `origin`(不再只比较 `origin.name`)。
62
+
63
+ ### trait 相等性与哈希语义
64
+
65
+ - `src/calcit/calcit_trait.rs`
66
+ - `PartialEq` 从 name-only 改为结构化比较。
67
+ - `Hash` 同步到相同结构维度。
68
+ - 增加注释,说明默认函数比较策略。
69
+
70
+ ### 相关一致性修正(同一工作区变更)
71
+
72
+ - `src/runner/preprocess.rs`
73
+ - 修复重命名后的测试守卫符号残留(`DynTraitCheckGuard` -> `WarnDynMethodGuard`)。
74
+
75
+ ## 验证
76
+
77
+ - `cargo fmt`
78
+ - `cargo test -q`
79
+ - 结果:当前工作区下测试全部通过。
80
+
81
+ ## 兼容性与风险
82
+
83
+ - 该行为变化是有意为之:同名但结构不同的 trait 不再被视为相等。
84
+ - 默认函数体差异仍不纳入 Eq/Hash。
85
+ - 方法分发优先级规则保持不变。
86
+
87
+ ## 后续可选项
88
+
89
+ 1. 如有需要,可补强默认实现身份比较(例如优先比较 def ref)。
90
+ 2. 增加“同名不同结构 trait”相等性的显式测试用例。
91
+ 3. 继续收敛 `warn-dyn-method` 全量运行中的剩余动态调用告警。
@@ -0,0 +1,33 @@
1
+ # 2026-02-13 19:26 文档与测试补充(跟进)
2
+
3
+ ## 背景
4
+
5
+ 在完成 trait/macro 相关优化(P1/P2/P3)后,补做“是否需要补充测试与文档”的审计。
6
+
7
+ ## 本次结论
8
+
9
+ 需要补充,且已补最小必要范围:
10
+
11
+ 1. 文档:
12
+ - `guidebook/docs/features/macros.md`
13
+ - 增加 `with-gensyms` 用法示例。
14
+ - 增加 `macroexpand`/`macroexpand-1`/`macroexpand-all` 的展开链路说明(stderr)。
15
+ - `guidebook/docs/features/traits.md`
16
+ - 增加 `warn-dyn-method` 下的新增诊断说明。
17
+ - 明确 `&trait-call` 按 impl 的 trait origin 匹配,而非仅按名称文本匹配。
18
+
19
+ 2. 测试:
20
+ - `src/calcit/calcit_trait.rs`
21
+ - 新增 default identity 单测:
22
+ - `def_ref` 相同 => trait 相等且 hash 一致;
23
+ - 无 `def_ref` 时走函数元信息 fallback,并校验等价/不等价边界。
24
+
25
+ ## 验证
26
+
27
+ - `cargo test -q calcit_trait::tests` 通过。
28
+ - `yarn check-all` 通过(本轮补丁后再跑)。
29
+
30
+ ## 经验点
31
+
32
+ - 文档应及时反映“诊断开关”边界(默认流程 vs `warn-dyn-method`)。
33
+ - identity 规则变化必须配对 hash/eq 回归测试,避免后续 refactor 引入行为漂移。
@@ -0,0 +1,39 @@
1
+ # 2026-02-14 20:50 JS codegen recursion + tag migration
2
+
3
+ ## 背景
4
+
5
+ 在 trait/macro 改进后,继续针对 JavaScript 编译目标做性能与可维护性优化。
6
+
7
+ ## 本次改动
8
+
9
+ 1. 参数个数报错模板统一
10
+
11
+ - 在 runtime 新增 `_args_fewer_throw` / `_args_between_throw`,保留 `_args_throw`。
12
+ - emitter 中 `too few/too many` 模板改为调用统一 helper,错误信息继续携带期望与实际参数个数。
13
+
14
+ 2. rest 参数处理优化
15
+
16
+ - 对 `&` rest 参数,仅在函数体实际引用时才注入 `arrayToList` 转换,避免不必要转换。
17
+ - 修复符号引用检测边界:`contains_symbol` 增加 `Calcit::Local` 分支。
18
+
19
+ 3. tail recursion 模板优化
20
+
21
+ - watchdog 从每轮检查改为周期检查(`(times & 1023) === 0` 时再判断上限)。
22
+ - 对无 rest/optional 的固定参数函数,recur 回填改为索引赋值(`arg = ret.args[i]`),减少解构开销。
23
+
24
+ 4. tag 初始化一次迁移
25
+
26
+ - runtime 新增 `init_tags` 与全局 tag 缓存。
27
+ - emitter 统一改为 `const _t_ = init_tags([...])` / `$clt.init_tags([...])`。
28
+ - 移除每模块内联 `forEach + newTag` 初始化模板。
29
+ - 修复 `calcit.core` 目标缺失导入,补上 `init_tags` import。
30
+
31
+ ## 验证
32
+
33
+ - `yarn check-all` 通过。
34
+ - 额外执行了 JS 微基准(`test-recursion.main/test_loop`)用于观察 recursion 模板优化影响。
35
+
36
+ ## 经验点
37
+
38
+ - JS 生成模板优化要先保证语义完整,再做结构性收敛(helper 化/去重复)。
39
+ - 对 codegen 的性能优化,建议配套微基准,并与 baseline 做同脚本对比。
@@ -0,0 +1,63 @@
1
+ # 2026-02-14 23:58 emit_js modularization + shift-left contracts
2
+
3
+ ## Scope
4
+
5
+ This round focuses on two tracks:
6
+
7
+ 1. Continue low-risk modularization of `src/codegen/emit_js.rs` without changing runtime semantics.
8
+ 2. Introduce AI-oriented "shift-left" validation contracts for expected warning/error paths.
9
+
10
+ ## What changed
11
+
12
+ ### Rust codegen modularization
13
+
14
+ - Removed duplicate legacy module file:
15
+ - `src/calcit/struct.rs`
16
+ - Split helpers from `emit_js.rs` into cohesive submodules:
17
+ - `src/codegen/emit_js/tags.rs`
18
+ - `src/codegen/emit_js/symbols.rs`
19
+ - `src/codegen/emit_js/paths.rs`
20
+ - `src/codegen/emit_js/runtime.rs`
21
+ - `src/codegen/emit_js/args.rs`
22
+ - `src/codegen/emit_js/deps.rs`
23
+ - `src/codegen/emit_js/helpers.rs`
24
+ - Kept behavior stable by only moving logic and wiring imports/call sites.
25
+ - Added targeted unit tests in extracted modules.
26
+
27
+ ### Runtime helper boundary cleanup (TS)
28
+
29
+ - Grouped runtime helpers into dedicated modules:
30
+ - `ts-src/js-arity-helpers.mts` (`_args_throw`, `_args_fewer_throw`, `_args_between_throw`)
31
+ - `ts-src/js-tag-helpers.mts` (`init_tags` + internal cache)
32
+ - Switched `ts-src/calcit.procs.mts` to re-export these modules, keeping public API names unchanged.
33
+
34
+ ### Test/validation workflow improvements
35
+
36
+ - Added contract-based shift-left script:
37
+ - `scripts/check-shift-left.mjs`
38
+ - Added npm script:
39
+ - `check-shift-left` in `package.json`
40
+ - This script enforces both:
41
+ - positive baseline must pass (`calcit/test.cirru -1`)
42
+ - selected negative cases must fail with expected diagnostic tokens.
43
+
44
+ ### Planning/documentation updates
45
+
46
+ - Expanded and updated roadmap status:
47
+ - `drafts/project-modernization-roadmap.md`
48
+ - Added AI-oriented long-term section for:
49
+ - immutable core constraints
50
+ - earlier error detection
51
+ - contractized diagnostics
52
+ - staged execution strategy.
53
+
54
+ ## Verification
55
+
56
+ - Ran targeted unit tests for extracted modules (`tags/symbols/paths/runtime/args/deps/helpers` where applicable).
57
+ - Repeated full validation with `yarn check-all` after each refactor batch.
58
+ - Ran `yarn check-shift-left` to validate diagnostic contracts.
59
+
60
+ ## Notes
61
+
62
+ - Some standalone `calcit/test-*.cirru` files are intentionally warning/error reproductions or independent package entries; they are not safe to inline into `test.cirru` module list directly.
63
+ - Using a separate shift-left contract script avoids namespace collisions and preserves intent.
@@ -0,0 +1,39 @@
1
+ # Macro Diagnostics Refinement (2026-02-15 19:41)
2
+
3
+ ## Summary
4
+
5
+ This change improves diagnostic quality for macro misuse and warning flows without changing macro semantics.
6
+
7
+ ## Key updates
8
+
9
+ - Added a shared diagnostics helper module:
10
+ - `src/diagnostics_help.rs`
11
+ - Centralizes message-to-help inference logic.
12
+ - Improved stack-rendered failure guidance:
13
+ - `src/call_stack.rs`
14
+ - Uses macro-context-aware help text.
15
+ - Keeps examples targeting user-facing macros over internal helper macros.
16
+ - Improved warning-path guidance:
17
+ - `src/bin/cr.rs`
18
+ - Reuses shared help inference for preprocessing/codegen warning blocks.
19
+ - Improved macro argument binding diagnostics in runtime:
20
+ - `src/runner.rs`
21
+ - Replaces low-level `Idx(...)` style arity failures with readable signatures and missing argument names.
22
+ - Refined macro-side error wording:
23
+ - `src/cirru/calcit-core.cirru`
24
+ - Better `list-match` branch-shape messages.
25
+ - Kept macro signatures intact where requested:
26
+ - `when`, `when-not`, `when-let` remain with explicit parameter forms.
27
+
28
+ ## Validation
29
+
30
+ - Spot checks executed with `cr eval` for macro misuse cases:
31
+ - `when`, `when-not`, `when-let`, `if-not`, `tag-match`, `record-match`, `list-match`, `cond`, `let`, `thread-first`, `thread-last`.
32
+ - Confirmed help text differentiates macro misuse vs proc/function arity warnings.
33
+ - Full regression passed:
34
+ - `yarn check-all` => `EXIT:0`.
35
+
36
+ ## Notes
37
+
38
+ - This iteration focuses on user-facing diagnostics quality.
39
+ - Behavior and compatibility are preserved; changes are primarily in error/help reporting paths.
@@ -0,0 +1,35 @@
1
+ # 2026-02-15 diagnostics 合并记录
2
+
3
+ 本文件合并以下 diagnostics 相关记录,避免碎片化:
4
+
5
+ - 2026-0215-1011-preprocess-diagnostic-v1.md
6
+ - 2026-0215-1026-cli-diag-json.md
7
+ - 2026-0215-1104-diag-expected-actual-action-let.md
8
+ - 2026-0215-1110-eval-diag-fallbacks.md
9
+ - 2026-0215-1116-macro-diagnostic-protocol.md
10
+ - 2026-0215-1134-macro-diag-expansion-if-defn-assert.md
11
+ - 2026-0215-1152-runtime-fallback-tag-match-arity.md
12
+ - 2026-0215-1202-warning-arity-fallback-compat.md
13
+ - 2026-0215-1211-warning-learning-fields.md
14
+ - 2026-0215-1238-convergence-help-docs-diag.md
15
+ - 2026-0215-1306-text-diagnostics-convergence-fix.md
16
+ - 2026-0215-1332-diagnostics-simplification-example-driven.md
17
+
18
+ ## 合并后的结论(当前保留)
19
+
20
+ - 已移除 `--diag json` 路径,CLI 统一使用文本诊断输出。
21
+ - warning 维持最小字段与文本展示,不再维护 JSON 结构演进。
22
+ - error 维持 call stack + examples 辅助定位,避免复杂规则映射。
23
+ - 文本模式保持简洁:优先 message/hint/location/stack,便于直接修复。
24
+
25
+ ## 本轮简化(减法)
26
+
27
+ - 移除 warning 侧复杂增强字段:`fingerprint/convergence/help/learning`。
28
+ - 移除 error 侧复杂增强字段:`fingerprint/convergence/help/learning` 与大段规则映射。
29
+ - 将 error 的 expected/actual/action 处理改为“hint 优先 + 通用 message 提取 + 最小 fallback”。
30
+
31
+ ## 设计原则(后续继续遵循)
32
+
33
+ - 诊断能力优先复用编译器已有信息(hint、message、stack、examples)。
34
+ - 尽量减少按错误码硬编码规则,避免语言层复杂度膨胀。
35
+ - LLM 修复入口尽量通用化:`query examples` + 最小可执行动作。
@@ -0,0 +1,40 @@
1
+ # 2026-02-16 20:43 core API/docs cleanup
2
+
3
+ ## Scope
4
+
5
+ - Cleaned outdated draft references and status notes.
6
+ - Aligned core internal API names around trait attachment (`*:impl-traits`).
7
+ - Fixed `wo-js-log` export/definition mismatch.
8
+ - Merged symbol-resolution note into drafts and removed duplicated doc file.
9
+
10
+ ## Key updates
11
+
12
+ 1. **drafts cleanup**
13
+ - Added `drafts/README.md` as a state index (active/review-needed/archived).
14
+ - Fixed stale links between `assert-types-plan.md` and `assert-types.md`.
15
+ - Marked `drafts/last-session.md` as archived historical snapshot.
16
+
17
+ 2. **core naming consistency**
18
+ - Updated internal entries in `src/cirru/calcit-core.cirru`:
19
+ - `&record:with-impls` -> `&record:impl-traits`
20
+ - `&tuple:with-impls` -> `&tuple:impl-traits`
21
+ - `&struct:with-impls` -> `&struct:impl-traits`
22
+ - `&enum:with-impls` -> `&enum:impl-traits`
23
+ - Updated `%::` doc hint to reference `&tuple:impl-traits`.
24
+
25
+ 3. **debug macro fix**
26
+ - Fixed `wo-js-log` entry to define `wo-js-log` (not `w-js-log`).
27
+
28
+ 4. **symbol note consolidation**
29
+ - Appended symbol-resolution appendix into `drafts/runtime-traits-plan.md`.
30
+ - Removed duplicated `docs/symbol-spec.md` after migration.
31
+
32
+ ## Validation notes
33
+
34
+ - Verified `wo-js-log` evaluates successfully.
35
+ - Verified `&struct:impl-traits` resolves as runtime proc.
36
+ - Confirmed old `&struct:with-impls` now reports unknown symbol (no compatibility layer, by design).
37
+
38
+ ## Follow-up
39
+
40
+ - If needed, add explicit migration note/changelog entry for removed `*-with-impls` internal names.
@@ -0,0 +1,20 @@
1
+ # Docs smoke cases and trait misuse error normalization
2
+
3
+ ## Summary
4
+
5
+ - Added executable docs smoke tests for key trait semantics.
6
+ - Integrated smoke tests into main test entry.
7
+ - Normalized high-frequency misuse messages with `Expected/Actual/Fix` guidance.
8
+ - Stabilized cross-runtime smoke assertions so `yarn check-all` passes in both eval and js modes.
9
+
10
+ ## Knowledge points
11
+
12
+ - `assert-traits` first argument is enforced at preprocess as local-only.
13
+ - `impl-traits` targets definition-level values (`struct`/`enum`), not instances.
14
+ - Error string matching in JS runtime should avoid host-error-dependent formatting; keep strict message-content checks in eval mode.
15
+
16
+ ## Validation
17
+
18
+ - `cargo fmt`
19
+ - `cargo run --bin cr -- calcit/test.cirru -1`
20
+ - `yarn check-all` (exit code 0)
@@ -291,6 +291,9 @@ export let hashFunction = (x) => {
291
291
  if (x instanceof CalcitImpl) {
292
292
  let base = defaultHash_impl;
293
293
  base = mergeValueHash(base, hashFunction(x.name));
294
+ if (x.origin != null) {
295
+ base = mergeValueHash(base, hashFunction(x.origin));
296
+ }
294
297
  for (let idx = 0; idx < x.fields.length; idx++) {
295
298
  base = mergeValueHash(base, hashFunction(x.fields[idx]));
296
299
  base = mergeValueHash(base, hashFunction(x.values[idx]));
@@ -670,6 +673,12 @@ export let _$n__$e_ = (x, y) => {
670
673
  if (x.name !== y.name) {
671
674
  return false;
672
675
  }
676
+ if ((x.origin == null) !== (y.origin == null)) {
677
+ return false;
678
+ }
679
+ if (x.origin != null && y.origin != null && x.origin.name.value !== y.origin.name.value) {
680
+ return false;
681
+ }
673
682
  if (!fieldsEqual(x.fields, y.fields)) {
674
683
  return false;
675
684
  }
@@ -23,6 +23,8 @@ export * from "./js-tuple.mjs";
23
23
  export * from "./js-trait.mjs";
24
24
  export * from "./custom-formatter.mjs";
25
25
  export * from "./js-cirru.mjs";
26
+ export * from "./js-arity-helpers.mjs";
27
+ export * from "./js-tag-helpers.mjs";
26
28
  export { _$n_compare } from "./js-primes.mjs";
27
29
  import { CalcitList, CalcitSliceList, foldl } from "./js-list.mjs";
28
30
  import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
@@ -198,14 +200,15 @@ export let defenum = (name, ...variants) => {
198
200
  const tags = entries.map((entry) => entry.tag);
199
201
  const values = entries.map((entry) => entry.payload);
200
202
  const prototype = new CalcitRecord(enumName, tags, values, null);
201
- return new CalcitEnum(prototype, null);
203
+ return new CalcitEnum(prototype);
202
204
  };
203
205
  export let _$n_impl_$o__$o_new = (name, ...pairs) => {
204
206
  if (name === undefined)
205
207
  throw new Error("&impl::new expected arguments");
206
- const implName = castTag(name);
208
+ const origin = name instanceof CalcitTrait ? name : null;
209
+ const implName = origin ? origin.name : castTag(name);
207
210
  if (pairs.length === 0) {
208
- return new CalcitImpl(implName, [], []);
211
+ return new CalcitImpl(implName, [], [], origin);
209
212
  }
210
213
  const entries = [];
211
214
  for (let idx = 0; idx < pairs.length; idx++) {
@@ -237,7 +240,7 @@ export let _$n_impl_$o__$o_new = (name, ...pairs) => {
237
240
  }
238
241
  const fields = entries.map((entry) => entry.tag);
239
242
  const values = entries.map((entry) => entry.value);
240
- return new CalcitImpl(implName, fields, values);
243
+ return new CalcitImpl(implName, fields, values, origin);
241
244
  };
242
245
  export let _$n_struct_$o__$o_new = (name, ...entries) => {
243
246
  return defstruct(name, ...entries);
@@ -451,7 +454,11 @@ export let _$n_tuple_$o_with_impls = function (x, y) {
451
454
  if (!(x instanceof CalcitTuple))
452
455
  throw new Error("&tuple:with-impls expects a tuple");
453
456
  const impl = coerce_impl(y, "&tuple:with-impls");
454
- return new CalcitTuple(x.tag, x.extra, [impl], x.enumPrototype);
457
+ let proto = x.enumPrototype;
458
+ if (proto == null) {
459
+ proto = new CalcitEnum(new CalcitRecord(newTag("anonymous-tuple"), [], [], new CalcitStruct(newTag("anonymous-tuple"), [], [])));
460
+ }
461
+ return new CalcitTuple(x.tag, x.extra, proto.withImpls(impl));
455
462
  };
456
463
  export let _$n_tuple_$o_impl_traits = function (x, ...traits) {
457
464
  if (traits.length < 1)
@@ -459,7 +466,13 @@ export let _$n_tuple_$o_impl_traits = function (x, ...traits) {
459
466
  if (!(x instanceof CalcitTuple))
460
467
  throw new Error("&tuple:impl-traits expects a tuple");
461
468
  const impls = traits.map((trait) => coerce_impl(trait, "&tuple:impl-traits"));
462
- return new CalcitTuple(x.tag, x.extra, x.impls.concat(impls), x.enumPrototype);
469
+ let proto = x.enumPrototype;
470
+ if (proto == null) {
471
+ const tagName = x.tag instanceof CalcitTag ? x.tag : newTag("tag");
472
+ const anyTypes = new CalcitSliceList(new Array(x.extra.length).fill(newTag("any")));
473
+ proto = new CalcitEnum(new CalcitRecord(newTag("anonymous-tuple"), [tagName], [anyTypes], new CalcitStruct(newTag("anonymous-tuple"), [tagName], [anyTypes])));
474
+ }
475
+ return new CalcitTuple(x.tag, x.extra, proto.withImpls(impls));
463
476
  };
464
477
  export let _$n_tuple_$o_enum = function (x) {
465
478
  if (arguments.length !== 1)
@@ -472,7 +485,7 @@ export let _$n_tuple_$o_enum = function (x) {
472
485
  if (x.enumPrototype instanceof CalcitEnum) {
473
486
  return x.enumPrototype;
474
487
  }
475
- return new CalcitEnum(x.enumPrototype, null);
488
+ return new CalcitEnum(x.enumPrototype);
476
489
  };
477
490
  const unwrap_enum_prototype = (enumPrototype, procName) => {
478
491
  if (enumPrototype instanceof CalcitEnum) {
@@ -584,7 +597,7 @@ export let _$n_record_$o_impls = function (xs) {
584
597
  if (arguments.length !== 1)
585
598
  throw new Error("&record:impls takes 1 argument");
586
599
  if (xs instanceof CalcitRecord)
587
- return new CalcitSliceList(xs.impls);
600
+ return new CalcitSliceList(xs.structRef.impls);
588
601
  throw new Error("&record:impls expected a record");
589
602
  };
590
603
  export let _$n_record_$o_impl_traits = function (xs, ...traits) {
@@ -593,7 +606,8 @@ export let _$n_record_$o_impl_traits = function (xs, ...traits) {
593
606
  if (!(xs instanceof CalcitRecord))
594
607
  throw new Error("&record:impl-traits expected a record");
595
608
  const impls = traits.map((trait) => coerce_impl(trait, "&record:impl-traits"));
596
- return new CalcitRecord(xs.name, xs.fields, xs.values, xs.impls.concat(impls));
609
+ const nextStruct = new CalcitStruct(xs.name, xs.fields, xs.structRef.fieldTypes, xs.structRef.impls.concat(impls));
610
+ return new CalcitRecord(xs.name, xs.fields, xs.values, nextStruct);
597
611
  };
598
612
  export let _$n_struct_$o_impl_traits = function (xs, ...traits) {
599
613
  if (traits.length < 1)
@@ -609,14 +623,41 @@ export let _$n_enum_$o_impl_traits = function (xs, ...traits) {
609
623
  throw new Error("&enum:impl-traits takes 2+ arguments");
610
624
  const addedImpls = traits.map((trait) => coerce_impl(trait, "&enum:impl-traits"));
611
625
  if (xs instanceof CalcitEnum) {
612
- const baseImpls = xs.impls ?? [];
613
- return new CalcitEnum(xs.prototype, baseImpls.concat(addedImpls));
626
+ return xs.withImpls(addedImpls);
614
627
  }
615
628
  if (xs instanceof CalcitRecord) {
616
- return new CalcitRecord(xs.name, xs.fields, xs.values, xs.impls.concat(addedImpls));
629
+ const nextStruct = new CalcitStruct(xs.name, xs.fields, xs.structRef.fieldTypes, xs.structRef.impls.concat(addedImpls));
630
+ return new CalcitRecord(xs.name, xs.fields, xs.values, nextStruct);
617
631
  }
618
632
  throw new Error("&enum:impl-traits expected an enum or enum record");
619
633
  };
634
+ export let _$n_impl_$o_origin = function (impl) {
635
+ if (arguments.length !== 1)
636
+ throw new Error("&impl:origin expected 1 argument");
637
+ if (impl instanceof CalcitImpl) {
638
+ return impl.origin ?? null;
639
+ }
640
+ throw new Error(`&impl:origin expected an impl, but received: ${toString(impl, true)}`);
641
+ };
642
+ export let _$n_impl_$o_get = function (impl, name) {
643
+ if (arguments.length !== 2)
644
+ throw new Error("&impl:get expected 2 arguments");
645
+ if (!(impl instanceof CalcitImpl)) {
646
+ throw new Error(`&impl:get expected an impl as first argument, but received: ${toString(impl, true)}`);
647
+ }
648
+ return impl.get(name);
649
+ };
650
+ export let _$n_impl_$o_nth = function (impl, index) {
651
+ if (arguments.length !== 2)
652
+ throw new Error("&impl:nth expected 2 arguments");
653
+ if (!(impl instanceof CalcitImpl)) {
654
+ throw new Error(`&impl:nth expected an impl as first argument, but received: ${toString(impl, true)}`);
655
+ }
656
+ if (typeof index !== "number" || !Number.isInteger(index) || index < 0) {
657
+ throw new Error(`&impl:nth expected a non-negative integer index, but received: ${toString(index, true)}`);
658
+ }
659
+ return impl.values[index];
660
+ };
620
661
  export let _$n_list_$o_assoc_before = function (xs, k, v) {
621
662
  if (arguments.length !== 3) {
622
663
  throw new Error("assoc takes 3 arguments");
@@ -1485,7 +1526,7 @@ export let _$n_js_object = (...xs) => {
1485
1526
  return ret;
1486
1527
  };
1487
1528
  export let _$o__$o_ = (tagName, ...extra) => {
1488
- return new CalcitTuple(tagName, extra, [], null);
1529
+ return new CalcitTuple(tagName, extra, null);
1489
1530
  };
1490
1531
  export let _PCT__$o__$o_ = (enumPrototype, tag, ...extra) => {
1491
1532
  const proto = assert_enum_tag_args("%::", enumPrototype, tag);
@@ -1505,7 +1546,7 @@ export let _PCT__$o__$o_ = (enumPrototype, tag, ...extra) => {
1505
1546
  throw new Error(`Expected variant definition to be a list, got ${variantDefinition}`);
1506
1547
  }
1507
1548
  const tupleEnumPrototype = enumPrototype instanceof CalcitEnum ? enumPrototype : proto;
1508
- return new CalcitTuple(tag, extra, [], tupleEnumPrototype);
1549
+ return new CalcitTuple(tag, extra, tupleEnumPrototype);
1509
1550
  };
1510
1551
  export let _PCT__PCT__$o__$o_ = (impl, enumPrototype, tag, ...extra) => {
1511
1552
  // Runtime validation: check if tag exists in enum and arity matches
@@ -1527,7 +1568,7 @@ export let _PCT__PCT__$o__$o_ = (impl, enumPrototype, tag, ...extra) => {
1527
1568
  }
1528
1569
  const tupleEnumPrototype = enumPrototype instanceof CalcitEnum ? enumPrototype : proto;
1529
1570
  const implValue = coerce_impl(impl, "%:: with impl");
1530
- return new CalcitTuple(tag, extra, [implValue], tupleEnumPrototype);
1571
+ return new CalcitTuple(tag, extra, tupleEnumPrototype.withImpls(implValue));
1531
1572
  };
1532
1573
  let calcit_builtin_impls = {
1533
1574
  number: null,
@@ -1574,7 +1615,7 @@ function lookup_impls(obj) {
1574
1615
  }
1575
1616
  else if (obj instanceof CalcitRecord) {
1576
1617
  tag = obj.name.toString();
1577
- impls = obj.impls;
1618
+ impls = obj.structRef.impls;
1578
1619
  }
1579
1620
  else if (obj instanceof CalcitTuple) {
1580
1621
  tag = obj.tag.toString();
@@ -1686,7 +1727,8 @@ export function _$n_inspect_methods(obj, note) {
1686
1727
  for (let k = 0; k < impl.fields.length; k++) {
1687
1728
  names.push("." + impl.fields[k].value);
1688
1729
  }
1689
- console.log(` #${i}: ${impl.name.toString()} (${names.join(" ")})`);
1730
+ const originName = impl.origin != null ? impl.origin.name.toString() : impl.name.toString();
1731
+ console.log(` #${i}: ${originName} (${names.join(" ")})`);
1690
1732
  }
1691
1733
  let ms = _$n_methods_of(obj);
1692
1734
  console.log(`\nAll methods (unique, high → low): ${ms.len()}`);
@@ -1716,7 +1758,7 @@ export function _$n_trait_call(traitDef, method, obj, ...args) {
1716
1758
  let idx = reverse ? impls.length - 1 : 0;
1717
1759
  while (reverse ? idx >= 0 : idx < impls.length) {
1718
1760
  const impl = impls[idx];
1719
- if (impl != null && impl.name.value === traitDef.name.value) {
1761
+ if (impl != null && impl.origin != null && impl.origin.name.value === traitDef.name.value) {
1720
1762
  const fn = impl.getOrNil(methodName);
1721
1763
  if (fn != null) {
1722
1764
  if (typeof fn !== "function") {
@@ -1864,8 +1906,5 @@ export let gensym = unavailableProc;
1864
1906
  export let macroexpand = unavailableProc;
1865
1907
  export let macroexpand_all = unavailableProc;
1866
1908
  export let _$n_get_calcit_running_mode = unavailableProc;
1867
- export let _args_throw = (name, expected, got) => {
1868
- return new Error(`\`${name}\` expected ${expected} params, got ${got}`);
1869
- };
1870
1909
  // already handled in code emitter
1871
1910
  export let raise = unavailableProc;