@calcit/procs 0.11.0 → 0.11.2

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/.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
@@ -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)
@@ -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";
@@ -637,6 +639,25 @@ export let _$n_impl_$o_origin = function (impl) {
637
639
  }
638
640
  throw new Error(`&impl:origin expected an impl, but received: ${toString(impl, true)}`);
639
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
+ };
640
661
  export let _$n_list_$o_assoc_before = function (xs, k, v) {
641
662
  if (arguments.length !== 3) {
642
663
  throw new Error("assoc takes 3 arguments");
@@ -1885,8 +1906,5 @@ export let gensym = unavailableProc;
1885
1906
  export let macroexpand = unavailableProc;
1886
1907
  export let macroexpand_all = unavailableProc;
1887
1908
  export let _$n_get_calcit_running_mode = unavailableProc;
1888
- export let _args_throw = (name, expected, got) => {
1889
- return new Error(`\`${name}\` expected ${expected} params, got ${got}`);
1890
- };
1891
1909
  // already handled in code emitter
1892
1910
  export let raise = unavailableProc;
@@ -0,0 +1,9 @@
1
+ export let _args_throw = (name, expected, got) => {
2
+ return new Error(`\`${name}\` expected ${expected} params, got ${got}`);
3
+ };
4
+ export let _args_fewer_throw = (name, min, got) => {
5
+ return new Error(`\`${name}\` expected at least ${min} params, got ${got}`);
6
+ };
7
+ export let _args_between_throw = (name, min, max, got) => {
8
+ return new Error(`\`${name}\` expected ${min}-${max} params, got ${got}`);
9
+ };
package/lib/js-record.mjs CHANGED
@@ -131,18 +131,28 @@ export let fieldsEqual = (xs, ys) => {
131
131
  return true;
132
132
  };
133
133
  export let _$n__PCT__$M_ = (proto, ...xs) => {
134
+ let recordProto;
134
135
  if (proto instanceof CalcitRecord) {
136
+ recordProto = proto;
137
+ }
138
+ else if (proto instanceof CalcitStruct) {
139
+ recordProto = new CalcitRecord(proto.name, proto.fields, new Array(proto.fields.length).fill(null), proto);
140
+ }
141
+ else {
142
+ throw new Error("Expected prototype to be a record");
143
+ }
144
+ {
135
145
  if (xs.length % 2 !== 0) {
136
146
  throw new Error("Expected even number of key/value");
137
147
  }
138
- if (xs.length !== proto.fields.length * 2) {
148
+ if (xs.length !== recordProto.fields.length * 2) {
139
149
  throw new Error("fields size does not match");
140
150
  }
141
- let values = new Array(proto.fields.length);
142
- for (let i = 0; i < proto.fields.length; i++) {
151
+ let values = new Array(recordProto.fields.length);
152
+ for (let i = 0; i < recordProto.fields.length; i++) {
143
153
  let idx = -1;
144
- let k = proto.fields[i];
145
- for (let j = 0; j < proto.fields.length; j++) {
154
+ let k = recordProto.fields[i];
155
+ for (let j = 0; j < recordProto.fields.length; j++) {
146
156
  if (k === castTag(xs[j * 2])) {
147
157
  idx = j;
148
158
  break;
@@ -156,10 +166,7 @@ export let _$n__PCT__$M_ = (proto, ...xs) => {
156
166
  }
157
167
  values[i] = xs[idx * 2 + 1];
158
168
  }
159
- return new CalcitRecord(proto.name, proto.fields, values, proto.structRef);
160
- }
161
- else {
162
- throw new Error("Expected prototype to be a record");
169
+ return new CalcitRecord(recordProto.name, recordProto.fields, values, recordProto.structRef);
163
170
  }
164
171
  };
165
172
  /// update record with new values
@@ -0,0 +1,15 @@
1
+ import { newTag } from "./calcit-data.mjs";
2
+ let _tag_cache = {};
3
+ export let init_tags = (arr) => {
4
+ let tags = {};
5
+ for (let idx = 0; idx < arr.length; idx++) {
6
+ let name = arr[idx];
7
+ let item = _tag_cache[name];
8
+ if (item === undefined) {
9
+ item = newTag(name);
10
+ _tag_cache[name] = item;
11
+ }
12
+ tags[name] = item;
13
+ }
14
+ return tags;
15
+ };
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.0.9",
@@ -8,11 +8,18 @@
8
8
  },
9
9
  "scripts": {
10
10
  "compile": "rm -rfv lib/* && tsc",
11
- "procs-link": "ln -s ../../ node_modules/@calcit/procs",
11
+ "procs-link": "ln -sfn ../../ node_modules/@calcit/procs",
12
12
  "cp-mac": "cargo build --release && rm -rfv builds/* && node scripts/cp-version.js && scp builds/* rsync-user@calcit-lang.org:/web-assets/repo/calcit-lang/binaries/macos/",
13
13
  "eval": "cargo run --bin cr -- eval",
14
+ "fmt-rs": "cargo fmt --all",
15
+ "lint-rs": "cargo clippy --all-targets -- -D warnings",
16
+ "test-rs": "cargo test -q",
17
+ "test-snippets": "cargo test -q snippets::tests",
18
+ "bench-recur-smoke": "cargo run --bin cr -- calcit/test.cirru -1 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));\"",
19
+ "check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
14
20
  "check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir",
15
21
  "try-rs": "cargo run --bin cr -- calcit/test.cirru -1",
22
+ "warn-dyn-method": "cargo run --bin cr -- calcit/test.cirru -1 --warn-dyn-method",
16
23
  "try-js-brk": "cargo run --bin cr -- calcit/test.cirru -1 js && node --inspect-brk js-out/main.mjs",
17
24
  "try-js": "cargo run --bin cr -- calcit/test.cirru -1 js && node js-out/main.mjs",
18
25
  "try-ir": "cargo run --bin cr -- calcit/test.cirru -1 js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.0.9",
@@ -8,11 +8,18 @@
8
8
  },
9
9
  "scripts": {
10
10
  "compile": "rm -rfv lib/* && tsc",
11
- "procs-link": "ln -s ../../ node_modules/@calcit/procs",
11
+ "procs-link": "ln -sfn ../../ node_modules/@calcit/procs",
12
12
  "cp-mac": "cargo build --release && rm -rfv builds/* && node scripts/cp-version.js && scp builds/* rsync-user@calcit-lang.org:/web-assets/repo/calcit-lang/binaries/macos/",
13
13
  "eval": "cargo run --bin cr -- eval",
14
+ "fmt-rs": "cargo fmt --all",
15
+ "lint-rs": "cargo clippy --all-targets -- -D warnings",
16
+ "test-rs": "cargo test -q",
17
+ "test-snippets": "cargo test -q snippets::tests",
18
+ "bench-recur-smoke": "cargo run --bin cr -- calcit/test.cirru -1 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));\"",
19
+ "check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
14
20
  "check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir",
15
21
  "try-rs": "cargo run --bin cr -- calcit/test.cirru -1",
22
+ "warn-dyn-method": "cargo run --bin cr -- calcit/test.cirru -1 --warn-dyn-method",
16
23
  "try-js-brk": "cargo run --bin cr -- calcit/test.cirru -1 js && node --inspect-brk js-out/main.mjs",
17
24
  "try-js": "cargo run --bin cr -- calcit/test.cirru -1 js && node js-out/main.mjs",
18
25
  "try-ir": "cargo run --bin cr -- calcit/test.cirru -1 js"
@@ -41,6 +41,8 @@ export * from "./js-tuple.mjs";
41
41
  export * from "./js-trait.mjs";
42
42
  export * from "./custom-formatter.mjs";
43
43
  export * from "./js-cirru.mjs";
44
+ export * from "./js-arity-helpers.mjs";
45
+ export * from "./js-tag-helpers.mjs";
44
46
  export { _$n_compare } from "./js-primes.mjs";
45
47
 
46
48
  import { CalcitList, CalcitSliceList, foldl } from "./js-list.mjs";
@@ -703,6 +705,25 @@ export let _$n_impl_$o_origin = function (impl: CalcitValue): CalcitValue {
703
705
  throw new Error(`&impl:origin expected an impl, but received: ${toString(impl, true)}`);
704
706
  };
705
707
 
708
+ export let _$n_impl_$o_get = function (impl: CalcitValue, name: CalcitValue): CalcitValue {
709
+ if (arguments.length !== 2) throw new Error("&impl:get expected 2 arguments");
710
+ if (!(impl instanceof CalcitImpl)) {
711
+ throw new Error(`&impl:get expected an impl as first argument, but received: ${toString(impl, true)}`);
712
+ }
713
+ return impl.get(name);
714
+ };
715
+
716
+ export let _$n_impl_$o_nth = function (impl: CalcitValue, index: CalcitValue): CalcitValue {
717
+ if (arguments.length !== 2) throw new Error("&impl:nth expected 2 arguments");
718
+ if (!(impl instanceof CalcitImpl)) {
719
+ throw new Error(`&impl:nth expected an impl as first argument, but received: ${toString(impl, true)}`);
720
+ }
721
+ if (typeof index !== "number" || !Number.isInteger(index) || index < 0) {
722
+ throw new Error(`&impl:nth expected a non-negative integer index, but received: ${toString(index, true)}`);
723
+ }
724
+ return impl.values[index];
725
+ };
726
+
706
727
  export let _$n_list_$o_assoc_before = function (xs: CalcitList | CalcitSliceList, k: number, v: CalcitValue): CalcitList {
707
728
  if (arguments.length !== 3) {
708
729
  throw new Error("assoc takes 3 arguments");
@@ -2027,9 +2048,5 @@ export let macroexpand = unavailableProc;
2027
2048
  export let macroexpand_all = unavailableProc;
2028
2049
  export let _$n_get_calcit_running_mode = unavailableProc;
2029
2050
 
2030
- export let _args_throw = (name: string, expected: number, got: number) => {
2031
- return new Error(`\`${name}\` expected ${expected} params, got ${got}`);
2032
- };
2033
-
2034
2051
  // already handled in code emitter
2035
2052
  export let raise = unavailableProc;
@@ -0,0 +1,11 @@
1
+ export let _args_throw = (name: string, expected: number, got: number) => {
2
+ return new Error(`\`${name}\` expected ${expected} params, got ${got}`);
3
+ };
4
+
5
+ export let _args_fewer_throw = (name: string, min: number, got: number) => {
6
+ return new Error(`\`${name}\` expected at least ${min} params, got ${got}`);
7
+ };
8
+
9
+ export let _args_between_throw = (name: string, min: number, max: number, got: number) => {
10
+ return new Error(`\`${name}\` expected ${min}-${max} params, got ${got}`);
11
+ };
@@ -135,20 +135,28 @@ export let fieldsEqual = (xs: Array<CalcitTag>, ys: Array<CalcitTag>): boolean =
135
135
  };
136
136
 
137
137
  export let _$n__PCT__$M_ = (proto: CalcitValue, ...xs: Array<CalcitValue>): CalcitValue => {
138
+ let recordProto: CalcitRecord;
138
139
  if (proto instanceof CalcitRecord) {
140
+ recordProto = proto;
141
+ } else if (proto instanceof CalcitStruct) {
142
+ recordProto = new CalcitRecord(proto.name, proto.fields, new Array(proto.fields.length).fill(null), proto);
143
+ } else {
144
+ throw new Error("Expected prototype to be a record");
145
+ }
146
+ {
139
147
  if (xs.length % 2 !== 0) {
140
148
  throw new Error("Expected even number of key/value");
141
149
  }
142
- if (xs.length !== proto.fields.length * 2) {
150
+ if (xs.length !== recordProto.fields.length * 2) {
143
151
  throw new Error("fields size does not match");
144
152
  }
145
153
 
146
- let values = new Array(proto.fields.length);
154
+ let values = new Array(recordProto.fields.length);
147
155
 
148
- for (let i = 0; i < proto.fields.length; i++) {
156
+ for (let i = 0; i < recordProto.fields.length; i++) {
149
157
  let idx = -1;
150
- let k = proto.fields[i];
151
- for (let j = 0; j < proto.fields.length; j++) {
158
+ let k = recordProto.fields[i];
159
+ for (let j = 0; j < recordProto.fields.length; j++) {
152
160
  if (k === castTag(xs[j * 2])) {
153
161
  idx = j;
154
162
  break;
@@ -164,9 +172,7 @@ export let _$n__PCT__$M_ = (proto: CalcitValue, ...xs: Array<CalcitValue>): Calc
164
172
  values[i] = xs[idx * 2 + 1];
165
173
  }
166
174
 
167
- return new CalcitRecord(proto.name, proto.fields, values, proto.structRef);
168
- } else {
169
- throw new Error("Expected prototype to be a record");
175
+ return new CalcitRecord(recordProto.name, recordProto.fields, values, recordProto.structRef);
170
176
  }
171
177
  };
172
178
 
@@ -0,0 +1,17 @@
1
+ import { CalcitTag, newTag } from "./calcit-data.mjs";
2
+
3
+ let _tag_cache: Record<string, CalcitTag> = {};
4
+
5
+ export let init_tags = (arr: string[]) => {
6
+ let tags: Record<string, CalcitTag> = {};
7
+ for (let idx = 0; idx < arr.length; idx++) {
8
+ let name = arr[idx];
9
+ let item = _tag_cache[name];
10
+ if (item === undefined) {
11
+ item = newTag(name);
12
+ _tag_cache[name] = item;
13
+ }
14
+ tags[name] = item;
15
+ }
16
+ return tags;
17
+ };