@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.
- package/.gitattributes +1 -0
- package/.yarn/install-state.gz +0 -0
- package/build.rs +74 -0
- package/editing-history/2026-0213-1841-trait-origin-structural-eq.md +91 -0
- package/editing-history/2026-0213-1926-docs-tests-followup.md +33 -0
- package/editing-history/2026-0214-2050-js-codegen-recursion-tag-migration.md +39 -0
- package/editing-history/2026-0214-2358-emit-js-modularization-shift-left.md +63 -0
- package/editing-history/2026-0215-1941-macro-diagnostics-refinement.md +39 -0
- package/editing-history/2026-0215-diagnostics-consolidated.md +35 -0
- package/editing-history/2026-0216-2043-core-api-docs-cleanup.md +40 -0
- package/editing-history/2026-0216-2128-docs-smoke-and-trait-errors.md +20 -0
- package/lib/calcit-data.mjs +9 -0
- package/lib/calcit.procs.mjs +60 -21
- package/lib/custom-formatter.mjs +2 -2
- package/lib/js-arity-helpers.mjs +9 -0
- package/lib/js-cirru.mjs +3 -3
- package/lib/js-enum.mjs +11 -5
- package/lib/js-impl.mjs +2 -1
- package/lib/js-record.mjs +32 -16
- package/lib/js-tag-helpers.mjs +15 -0
- package/lib/js-tuple.mjs +12 -10
- package/lib/package.json +9 -2
- package/package.json +9 -2
- package/ts-src/calcit-data.mts +9 -0
- package/ts-src/calcit.procs.mts +63 -23
- package/ts-src/custom-formatter.mts +2 -2
- package/ts-src/js-arity-helpers.mts +11 -0
- package/ts-src/js-cirru.mts +2 -4
- package/ts-src/js-enum.mts +11 -6
- package/ts-src/js-impl.mts +4 -1
- package/ts-src/js-primes.mts +2 -0
- package/ts-src/js-record.mts +32 -17
- package/ts-src/js-tag-helpers.mts +17 -0
- package/ts-src/js-tuple.mts +14 -11
package/.gitattributes
CHANGED
package/.yarn/install-state.gz
CHANGED
|
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)
|
package/lib/calcit-data.mjs
CHANGED
|
@@ -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
|
}
|
package/lib/calcit.procs.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
613
|
-
return new CalcitEnum(xs.prototype, baseImpls.concat(addedImpls));
|
|
626
|
+
return xs.withImpls(addedImpls);
|
|
614
627
|
}
|
|
615
628
|
if (xs instanceof CalcitRecord) {
|
|
616
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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;
|