@calcit/procs 0.11.8 → 0.12.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 -1
- package/.yarn/install-state.gz +0 -0
- package/build.rs +229 -11
- package/editing-history/2026-0305-0042-type-inference-argtypes-and-core-hints.md +54 -0
- package/editing-history/2026-0305-0115-calcit-core-generic-refinements.md +57 -0
- package/editing-history/2026-0305-1930-schema-migration-cr-edit.md +30 -0
- package/editing-history/2026-0305-1939-query-schema-adaptation.md +42 -0
- package/editing-history/2026-0306-0120-schema-hint-migration-batch.md +38 -0
- package/editing-history/2026-0306-1416-core-macro-schema-migration.md +20 -0
- package/editing-history/2026-0306-1552-core-schema-and-hintfn-migration.md +63 -0
- package/editing-history/2026-0306-1936-schema-normalization-and-preprocess-preload.md +12 -0
- package/editing-history/2026-0307-0000-migrate-hint-fn-to-schema.md +29 -0
- package/editing-history/2026-0307-0000-remove-enum-prototype-field.md +43 -0
- package/editing-history/2026-0307-0000-schema-args-type-enforcement.md +66 -0
- package/editing-history/2026-0307-0142-unit-type-and-builtin-schemas.md +82 -0
- package/editing-history/2026-0307-1302-calcit-agent-docs-update.md +32 -0
- package/editing-history/2026-0307-1652-decouple-snapshot-schema-to-calcit-type.md +112 -0
- package/editing-history/2026-0307-1821-snapshot-schema-validation.md +6 -0
- package/editing-history/2026-0307-1959-schema-def-kind-arity-validation.md +96 -0
- package/editing-history/2026-0308-0031-schema-generics-quote-normalization.md +35 -0
- package/editing-history/2026-0308-1905-schema-typeref-and-assert-migration.md +34 -0
- package/editing-history/2026-0308-2033-schema-wrapper-migration.md +45 -0
- package/editing-history/2026-0308-2315-late-schema-cleanup-and-warning-compat.md +27 -0
- package/editing-history/2026-0310-1040-edit-schema-primitive-tag-support.md +42 -0
- package/editing-history/202603091753-schema-type-fail-tests.md +17 -0
- package/editing-history/202603091819-add-test-fail-script.md +5 -0
- package/editing-history/202603091944-split-type-fail-tests.md +8 -0
- package/lib/package.json +3 -2
- package/package.json +3 -2
- /package/editing-history/{2026-0304-generics-fn-typevar-identity.md → 2026-0304-0000-generics-fn-typevar-identity.md} +0 -0
package/.gitattributes
CHANGED
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/build.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use cirru_edn::{Edn, from_edn};
|
|
1
|
+
use cirru_edn::{Edn, EdnRecordView, from_edn};
|
|
2
2
|
use cirru_parser::Cirru;
|
|
3
3
|
use serde::{Deserialize, Serialize};
|
|
4
4
|
use std::collections::HashMap;
|
|
@@ -24,6 +24,8 @@ pub struct CodeEntry {
|
|
|
24
24
|
#[serde(default)]
|
|
25
25
|
pub examples: Vec<Cirru>,
|
|
26
26
|
pub code: Cirru,
|
|
27
|
+
#[serde(default)]
|
|
28
|
+
pub schema: Option<Edn>,
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
@@ -41,34 +43,250 @@ pub struct Snapshot {
|
|
|
41
43
|
pub files: HashMap<String, FileInSnapShot>,
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
fn format_edn_preview(value: &Edn) -> String {
|
|
47
|
+
let raw = cirru_edn::format(value, true).unwrap_or_else(|_| format!("{value:?}"));
|
|
48
|
+
const LIMIT: usize = 220;
|
|
49
|
+
if raw.chars().count() > LIMIT {
|
|
50
|
+
let truncated = raw.chars().take(LIMIT).collect::<String>();
|
|
51
|
+
format!("{truncated}…")
|
|
52
|
+
} else {
|
|
53
|
+
raw
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn truncate_preview(raw: &str, limit: usize) -> String {
|
|
58
|
+
if raw.chars().count() > limit {
|
|
59
|
+
let truncated = raw.chars().take(limit).collect::<String>();
|
|
60
|
+
format!("{truncated}…")
|
|
61
|
+
} else {
|
|
62
|
+
raw.to_owned()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fn truncate_edn_error_nodes(message: &str) -> String {
|
|
67
|
+
const NODE_LIMIT: usize = 200;
|
|
68
|
+
|
|
69
|
+
message
|
|
70
|
+
.lines()
|
|
71
|
+
.map(|line| {
|
|
72
|
+
if let Some((prefix, preview)) = line.split_once("Node: ") {
|
|
73
|
+
format!("{prefix}Node: {}", truncate_preview(preview, NODE_LIMIT))
|
|
74
|
+
} else {
|
|
75
|
+
line.to_owned()
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
.collect::<Vec<_>>()
|
|
79
|
+
.join("\n")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fn format_edn_error<E: std::fmt::Display>(error: E) -> String {
|
|
83
|
+
truncate_edn_error_nodes(&error.to_string())
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
fn schema_path_label(path: &[String]) -> String {
|
|
87
|
+
if path.is_empty() { "<root>".to_owned() } else { path.join("") }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fn map_key_path_segment(key: &Edn) -> String {
|
|
91
|
+
match key {
|
|
92
|
+
Edn::Tag(tag) => format!(".{}", tag.ref_str()),
|
|
93
|
+
Edn::Str(text) => format!(".{text}"),
|
|
94
|
+
Edn::Symbol(text) => format!(".{text}"),
|
|
95
|
+
_ => ".<key>".to_owned(),
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Convert a schema Edn value (either old Quote-wrapped or new direct map) into Edn map form.
|
|
100
|
+
fn parse_schema_from_edn(value: &Edn, owner: &str) -> Result<Edn, String> {
|
|
101
|
+
// Old format: Edn::Quote wrapping Cirru — convert to direct map Edn
|
|
102
|
+
if let Ok(cirru) = from_edn::<Cirru>(value.clone()) {
|
|
103
|
+
let text = cirru_parser::format(&[cirru], true.into())
|
|
104
|
+
.map_err(|e| format!("{owner}: failed to format quoted schema before validation: {}", format_edn_error(e)))?;
|
|
105
|
+
let parsed = cirru_edn::parse(&text).map_err(|e| {
|
|
106
|
+
format!(
|
|
107
|
+
"{owner}: failed to parse quoted schema after formatting: {}; schema={}",
|
|
108
|
+
format_edn_error(e),
|
|
109
|
+
truncate_preview(&text, 200)
|
|
110
|
+
)
|
|
111
|
+
})?;
|
|
112
|
+
validate_schema_edn_no_legacy_quotes(&parsed, owner)?;
|
|
113
|
+
return Ok(parsed);
|
|
114
|
+
}
|
|
115
|
+
// New format: already a direct Edn map
|
|
116
|
+
validate_schema_edn_no_legacy_quotes(value, owner)?;
|
|
117
|
+
Ok(value.clone())
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
fn validate_schema_edn_no_legacy_quotes(value: &Edn, owner: &str) -> Result<(), String> {
|
|
121
|
+
fn walk(value: &Edn, owner: &str, path: &mut Vec<String>) -> Result<(), String> {
|
|
122
|
+
match value {
|
|
123
|
+
Edn::Symbol(s) => {
|
|
124
|
+
if s.starts_with('\'') {
|
|
125
|
+
let inner = s.trim_start_matches('\'');
|
|
126
|
+
return Err(format!(
|
|
127
|
+
"{owner}: invalid schema generic symbol `{s}` at {}. Use source syntax like `'{inner}`, but store it as plain EDN symbol `{inner}`.",
|
|
128
|
+
schema_path_label(path)
|
|
129
|
+
));
|
|
130
|
+
}
|
|
131
|
+
Ok(())
|
|
132
|
+
}
|
|
133
|
+
Edn::List(xs) => {
|
|
134
|
+
for (idx, item) in xs.0.iter().enumerate() {
|
|
135
|
+
path.push(format!("[{idx}]"));
|
|
136
|
+
walk(item, owner, path)?;
|
|
137
|
+
path.pop();
|
|
138
|
+
}
|
|
139
|
+
Ok(())
|
|
140
|
+
}
|
|
141
|
+
Edn::Map(map) => {
|
|
142
|
+
for (k, v) in &map.0 {
|
|
143
|
+
path.push(map_key_path_segment(k));
|
|
144
|
+
walk(v, owner, path)?;
|
|
145
|
+
path.pop();
|
|
146
|
+
}
|
|
147
|
+
Ok(())
|
|
148
|
+
}
|
|
149
|
+
Edn::Tuple(view) => {
|
|
150
|
+
path.push(".tag".to_owned());
|
|
151
|
+
walk(view.tag.as_ref(), owner, path)?;
|
|
152
|
+
path.pop();
|
|
153
|
+
for (idx, item) in view.extra.iter().enumerate() {
|
|
154
|
+
path.push(format!("[{idx}]"));
|
|
155
|
+
walk(item, owner, path)?;
|
|
156
|
+
path.pop();
|
|
157
|
+
}
|
|
158
|
+
Ok(())
|
|
159
|
+
}
|
|
160
|
+
Edn::Set(set) => {
|
|
161
|
+
for (idx, item) in set.0.iter().enumerate() {
|
|
162
|
+
path.push(format!("[#{idx}]"));
|
|
163
|
+
walk(item, owner, path)?;
|
|
164
|
+
path.pop();
|
|
165
|
+
}
|
|
166
|
+
Ok(())
|
|
167
|
+
}
|
|
168
|
+
Edn::Record(_) => Ok(()),
|
|
169
|
+
_ => Ok(()),
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let mut path = vec![];
|
|
174
|
+
walk(value, owner, &mut path)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
fn parse_code_entry(edn: Edn, owner: &str) -> Result<CodeEntry, String> {
|
|
178
|
+
let record: EdnRecordView = match edn {
|
|
179
|
+
Edn::Record(r) => r,
|
|
180
|
+
other => return Err(format!("{owner}: expected CodeEntry record, got {}", format_edn_preview(&other))),
|
|
181
|
+
};
|
|
182
|
+
let mut doc = String::new();
|
|
183
|
+
let mut examples: Vec<Cirru> = vec![];
|
|
184
|
+
let mut code: Option<Cirru> = None;
|
|
185
|
+
let mut schema: Option<Edn> = None;
|
|
186
|
+
for (key, value) in &record.pairs {
|
|
187
|
+
match key.arc_str().as_ref() {
|
|
188
|
+
"doc" => doc = from_edn(value.clone()).map_err(|e| format!("{owner}: invalid `:doc`: {e}"))?,
|
|
189
|
+
"examples" => examples = from_edn(value.clone()).map_err(|e| format!("{owner}: invalid `:examples`: {e}"))?,
|
|
190
|
+
"code" => code = Some(from_edn(value.clone()).map_err(|e| format!("{owner}: invalid `:code`: {e}"))?),
|
|
191
|
+
"schema" => {
|
|
192
|
+
if !matches!(value, Edn::Nil) {
|
|
193
|
+
schema = Some(parse_schema_from_edn(value, owner).map_err(|e| format!("{owner}: invalid `:schema`: {e}"))?);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
_ => {}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
Ok(CodeEntry {
|
|
200
|
+
doc,
|
|
201
|
+
examples,
|
|
202
|
+
code: code.ok_or_else(|| format!("{owner}: missing `:code` field in CodeEntry"))?,
|
|
203
|
+
schema,
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fn parse_file_in_snapshot(edn: Edn, file_name: &str) -> Result<FileInSnapShot, String> {
|
|
208
|
+
let record: EdnRecordView = match edn {
|
|
209
|
+
Edn::Record(r) => r,
|
|
210
|
+
other => {
|
|
211
|
+
return Err(format!(
|
|
212
|
+
"{file_name}: expected FileEntry record, got {}",
|
|
213
|
+
format_edn_preview(&other)
|
|
214
|
+
));
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
let mut ns: Option<CodeEntry> = None;
|
|
218
|
+
let mut defs: HashMap<String, CodeEntry> = HashMap::new();
|
|
219
|
+
for (key, value) in &record.pairs {
|
|
220
|
+
match key.arc_str().as_ref() {
|
|
221
|
+
"ns" => ns = Some(parse_code_entry(value.clone(), &format!("{file_name}/:ns"))?),
|
|
222
|
+
"defs" => {
|
|
223
|
+
let map = match value {
|
|
224
|
+
Edn::Map(m) => m,
|
|
225
|
+
other => return Err(format!("{file_name}: expected `:defs` map, got {}", format_edn_preview(other))),
|
|
226
|
+
};
|
|
227
|
+
for (def_key, def_value) in &map.0 {
|
|
228
|
+
let name: String = from_edn(def_key.clone()).map_err(|e| format!("{file_name}: invalid def key: {e}"))?;
|
|
229
|
+
let owner = format!("{file_name}/{name}");
|
|
230
|
+
defs.insert(name, parse_code_entry(def_value.clone(), &owner)?);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
_ => {}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
Ok(FileInSnapShot {
|
|
237
|
+
ns: ns.ok_or_else(|| format!("{file_name}: missing `:ns` field in FileEntry"))?,
|
|
238
|
+
defs,
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fn parse_files(edn: Edn) -> Result<HashMap<String, FileInSnapShot>, String> {
|
|
243
|
+
match edn {
|
|
244
|
+
Edn::Map(map) => {
|
|
245
|
+
let mut result = HashMap::with_capacity(map.0.len());
|
|
246
|
+
for (key, value) in map.0 {
|
|
247
|
+
let name: String = from_edn(key).map_err(|e| format!("invalid file key: {e}"))?;
|
|
248
|
+
result.insert(name.clone(), parse_file_in_snapshot(value, &name)?);
|
|
249
|
+
}
|
|
250
|
+
Ok(result)
|
|
251
|
+
}
|
|
252
|
+
other => Err(format!("snapshot `:files` must be a map, got {}", format_edn_preview(&other))),
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
44
256
|
fn main() {
|
|
45
257
|
println!("cargo:rerun-if-changed=src/cirru/calcit-core.cirru");
|
|
46
258
|
|
|
47
259
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
48
260
|
let dest_path = Path::new(&out_dir).join("calcit-core.rmp");
|
|
49
261
|
|
|
50
|
-
let core_content =
|
|
51
|
-
|
|
262
|
+
let core_content =
|
|
263
|
+
fs::read_to_string("src/cirru/calcit-core.cirru").unwrap_or_else(|e| panic!("failed to read src/cirru/calcit-core.cirru: {e}"));
|
|
264
|
+
let core_data = cirru_edn::parse(&core_content)
|
|
265
|
+
.unwrap_or_else(|e| panic!("failed to parse src/cirru/calcit-core.cirru as Cirru EDN: {}", format_edn_error(e)));
|
|
52
266
|
|
|
53
267
|
// Minimal logic to convert Edn to Snapshot as in src/snapshot.rs
|
|
54
|
-
let data = core_data
|
|
55
|
-
|
|
268
|
+
let data = core_data
|
|
269
|
+
.view_map()
|
|
270
|
+
.unwrap_or_else(|e| panic!("calcit-core snapshot root must be a map: {e}"));
|
|
271
|
+
let pkg: String = from_edn(data.get_or_nil("package")).unwrap_or_else(|e| panic!("failed to parse calcit-core `:package`: {e}"));
|
|
56
272
|
let about = match data.get_or_nil("about") {
|
|
57
273
|
Edn::Nil => None,
|
|
58
|
-
value => Some(from_edn::<String>(value).
|
|
274
|
+
value => Some(from_edn::<String>(value).unwrap_or_else(|e| panic!("failed to parse calcit-core `:about`: {e}"))),
|
|
59
275
|
};
|
|
60
276
|
|
|
61
|
-
let files
|
|
277
|
+
let files = parse_files(data.get_or_nil("files")).unwrap_or_else(|e| panic!("failed to parse calcit-core `:files`: {e}"));
|
|
62
278
|
|
|
63
279
|
let snapshot = Snapshot {
|
|
64
280
|
package: pkg,
|
|
65
281
|
about,
|
|
66
|
-
configs: from_edn(data.get_or_nil("configs")).
|
|
67
|
-
entries: from_edn(data.get_or_nil("entries")).
|
|
282
|
+
configs: from_edn(data.get_or_nil("configs")).unwrap_or_else(|e| panic!("failed to parse calcit-core `:configs`: {e}")),
|
|
283
|
+
entries: from_edn(data.get_or_nil("entries")).unwrap_or_else(|e| panic!("failed to parse calcit-core `:entries`: {e}")),
|
|
68
284
|
files,
|
|
69
285
|
};
|
|
70
286
|
|
|
71
287
|
let mut buf = Vec::new();
|
|
72
|
-
snapshot
|
|
73
|
-
|
|
288
|
+
snapshot
|
|
289
|
+
.serialize(&mut rmp_serde::Serializer::new(&mut buf))
|
|
290
|
+
.unwrap_or_else(|e| panic!("failed to serialize embedded calcit-core snapshot: {e}"));
|
|
291
|
+
fs::write(dest_path, buf).unwrap_or_else(|e| panic!("failed to write embedded calcit-core snapshot: {e}"));
|
|
74
292
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# 2026-03-05 类型推断增强记录
|
|
2
|
+
|
|
3
|
+
## 本次目标
|
|
4
|
+
|
|
5
|
+
- 在 upstream 重置后,重新加强类型推断能力,并保证 debug/release/全量检查都稳定通过。
|
|
6
|
+
|
|
7
|
+
## 关键改动
|
|
8
|
+
|
|
9
|
+
### 1) 预处理阶段 Proc 返回类型传播增强
|
|
10
|
+
|
|
11
|
+
在 `src/runner/preprocess.rs` 的 `infer_type_from_expr` 中补齐多类内建过程返回类型推断:
|
|
12
|
+
|
|
13
|
+
- List 保型:`sort`、`&list:concat`、`&list:assoc`、`&list:assoc-before`、`&list:assoc-after`、`&list:dissoc`。
|
|
14
|
+
- 固定返回:`range -> list<number>`,`split/split-lines -> list<string>`,`&map:to-list -> list`。
|
|
15
|
+
- Map 保型:`&map:assoc`、`&map:dissoc`、`&merge`、`&merge-non-nil`、`&map:diff-new`。
|
|
16
|
+
- Set 保型:`&include`、`&exclude`、`&difference`、`&union`、`&set:intersection`。
|
|
17
|
+
|
|
18
|
+
### 2) defn 参数类型提示传播修复(避免栈溢出)
|
|
19
|
+
|
|
20
|
+
在 `src/builtins/syntax.rs` 中增强 `defn` 的 `arg_types` 回退策略:
|
|
21
|
+
|
|
22
|
+
- 第一层:沿用原逻辑,从 body 中 `assert-type` 模式提取。
|
|
23
|
+
- 第二层:当全是 Dynamic 时,从参数列表 Local 的 `type_info` 读取。
|
|
24
|
+
- 第三层:当仍全是 Dynamic 时,从**预处理后 body 顶层 Local**提取(`assert-type` 被预处理成 typed local 的场景)。
|
|
25
|
+
- 为避免核心加载路径栈压力,`calcit.core` 命名空间跳过该额外回退扫描。
|
|
26
|
+
|
|
27
|
+
### 3) calcit-core 类型提示细化
|
|
28
|
+
|
|
29
|
+
在 `src/cirru/calcit-core.cirru` 为以下列表保型函数补充泛型提示:
|
|
30
|
+
|
|
31
|
+
- `distinct`
|
|
32
|
+
- `drop`
|
|
33
|
+
- `repeat`
|
|
34
|
+
- `reverse`
|
|
35
|
+
- `take`
|
|
36
|
+
- `take-last`
|
|
37
|
+
|
|
38
|
+
统一使用 `hint-fn (generics 'T) $ return-type (:: :list 'T)`,并将输入列表参数改为 `assert-type ... (:: :list 'T)`。
|
|
39
|
+
|
|
40
|
+
### 4) 测试用例一致性调整
|
|
41
|
+
|
|
42
|
+
`calcit/test-types.cirru` 中 `test-arg-type-hints` 示例从故意错误参数改为合法参数,避免新检查路径把 warning 升级为阻断导致全量检查失败。
|
|
43
|
+
|
|
44
|
+
## 过程中的关键排障结论
|
|
45
|
+
|
|
46
|
+
- 多次尝试在 `preprocess_defn` 内直接重建/替换参数 AST 或引入额外全局缓存时,容易触发主流程栈溢出。
|
|
47
|
+
- 最终采用“最小干预 + 避开 core 热路径回退扫描”的方式稳定通过。
|
|
48
|
+
|
|
49
|
+
## 验证结果
|
|
50
|
+
|
|
51
|
+
- `cargo test` 通过。
|
|
52
|
+
- `yarn check-all` 通过(`EXIT:0`)。
|
|
53
|
+
- `cargo build --release` + `./target/release/cr calcit/test.cirru -1` 通过。
|
|
54
|
+
- `./target/debug/cr calcit/test.cirru -1` 通过。
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 2026-03-05 calict.core 泛型签名细化
|
|
2
|
+
|
|
3
|
+
## 目标
|
|
4
|
+
|
|
5
|
+
在不改变运行时语义的前提下,继续收紧 `calcit-core.cirru` 中 `defn` 的类型签名,让类型推断更稳定、错误提示更精确。
|
|
6
|
+
|
|
7
|
+
## 主要改动
|
|
8
|
+
|
|
9
|
+
本次聚焦于“保型列表函数”“可选返回值函数”“函数组合器”“map 回调函数形状”四类。
|
|
10
|
+
|
|
11
|
+
### 一、列表保型与可选返回
|
|
12
|
+
|
|
13
|
+
- `distinct`, `drop`, `repeat`, `reverse`, `take`, `take-last`
|
|
14
|
+
- 统一到 `hint-fn (generics 'T) $ return-type (:: :list 'T)`。
|
|
15
|
+
- `&list:find-last`
|
|
16
|
+
- 改为 `(:: :optional 'T)`。
|
|
17
|
+
- `&list:find-last-index`, `&list:last-index-of`, `index-of`
|
|
18
|
+
- 改为 `(:: :optional :number)`。
|
|
19
|
+
- `&list:max`, `&list:min`
|
|
20
|
+
- 改为 `(:: :optional 'T)`。
|
|
21
|
+
|
|
22
|
+
### 二、列表/映射映射函数泛型化
|
|
23
|
+
|
|
24
|
+
- `&list:map`
|
|
25
|
+
- 从宽泛 `:list/:fn` 收紧到 `('T -> 'U)`,返回 `(:: :list 'U)`。
|
|
26
|
+
- `map`
|
|
27
|
+
- 回调从 `('T -> 'T)` 扩展为 `('T -> 'U)`,与 map 语义一致。
|
|
28
|
+
- `map-indexed`
|
|
29
|
+
- 返回类型显式为 `(:: :list 'U)`,回调签名改为 `(:number 'T) -> 'U`。
|
|
30
|
+
|
|
31
|
+
### 三、拼接与组合函数
|
|
32
|
+
|
|
33
|
+
- `concat`, `conj`, `interleave`, `join`
|
|
34
|
+
- 输入与输出统一到同一元素类型 `'T` 的列表。
|
|
35
|
+
- `&list:apply`
|
|
36
|
+
- 收紧为 `xs: list<T>, fs: list<(T -> U)>`,返回 `list<U>`。
|
|
37
|
+
|
|
38
|
+
### 四、函数组合器与 map helper
|
|
39
|
+
|
|
40
|
+
- `&fn:apply`, `&fn:bind`, `&fn:map`
|
|
41
|
+
- 增加 `('A 'B 'C)` 级别泛型,表达组合器的真实输入输出关系。
|
|
42
|
+
- `&map:map-list`
|
|
43
|
+
- 返回改为 `(:: :list 'U)`,并补充 `f/acc/pair` 类型约束。
|
|
44
|
+
- `&list:map-pair`
|
|
45
|
+
- 返回改为 `(:: :list 'U)`,`f` 改为 `('K 'V) -> 'U`。
|
|
46
|
+
- `&map:filter`, `&map:filter-kv`, `&map:map`
|
|
47
|
+
- 回调签名改为带输入/输出形状的泛型函数类型,减少 `:fn` 过宽带来的误判。
|
|
48
|
+
|
|
49
|
+
## 验证
|
|
50
|
+
|
|
51
|
+
日志均写入仓库内 `js-out/`:
|
|
52
|
+
|
|
53
|
+
- `yarn check-all > js-out/check-all.log`,`EXIT:0`
|
|
54
|
+
- `cargo build --release > js-out/release-build.log`
|
|
55
|
+
- `./target/release/cr calcit/test.cirru -1 > js-out/release-run.log`,`EXIT:0`
|
|
56
|
+
|
|
57
|
+
结论:本次改动仅增强静态类型信息,未改变运行行为,且全量检查通过。
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# 2026-03-05 19:30 schema 迁移记录
|
|
2
|
+
|
|
3
|
+
## 本次改动
|
|
4
|
+
|
|
5
|
+
- 持续使用 `cr edit schema` 迁移 `src/cirru/calcit-core.cirru` 中 `defn` 的 `:schema`。
|
|
6
|
+
- 统一命令形式为:
|
|
7
|
+
- entry 使用 `src/cirru/calcit-core.cirru`
|
|
8
|
+
- target 使用 `namespace/definition`
|
|
9
|
+
- schema 使用 pair map 语法:`{} (:kind :fn) (:args ...) (:return ...)`
|
|
10
|
+
- 对包含 `?` 的函数名(如 `set?`)在 shell 中使用引号,避免 zsh 通配展开。
|
|
11
|
+
|
|
12
|
+
## 迁移经验
|
|
13
|
+
|
|
14
|
+
- `cr -- calcit/test.cirru edit schema calcit.core/...` 会触发“只允许 app 包编辑”,改用 core 源文件作为 entry 可编辑 core namespace。
|
|
15
|
+
- schema map 在 `-e` 中不能写成 `:args ... :return ...` 平铺键值,必须写成 `(:args ...)`、`(:return ...)` 的 pair。
|
|
16
|
+
- 批量链式命令中若某个目标失败,会中断后续目标;迁移后需要用搜索确认实际落盘范围。
|
|
17
|
+
|
|
18
|
+
## 本轮补充的 schema(代表性)
|
|
19
|
+
|
|
20
|
+
- `section-by`, `select-keys`, `set?`, `slice`, `some-in?`, `some?`
|
|
21
|
+
- `str`, `str-spaced`, `string?`, `strip-prefix`, `strip-suffix`
|
|
22
|
+
- `struct?`, `symbol?`, `syntax?`, `tag?`, `tagging-edn`
|
|
23
|
+
- `take`, `take-last`, `thread-step?`, `tuple?`, `turn-str`
|
|
24
|
+
- `union`, `unselect-keys`, `update`, `update-in`
|
|
25
|
+
- `option:map`, `optionally`, `pairs-map`, `range-bothway`, `result:map`, `vals`, `zipmap`
|
|
26
|
+
- `calcit.internal/normalize-trait-type`
|
|
27
|
+
|
|
28
|
+
## 验证
|
|
29
|
+
|
|
30
|
+
- 运行通过:`cargo test && yarn check-all`。
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# 2026-03-05 19:39 query schema 适配
|
|
2
|
+
|
|
3
|
+
## 改动概要
|
|
4
|
+
|
|
5
|
+
- 针对 `cr query` 工具链补充 schema 感知与展示,修改文件:
|
|
6
|
+
- `src/bin/cli_handlers/query.rs`
|
|
7
|
+
|
|
8
|
+
## 具体更新
|
|
9
|
+
|
|
10
|
+
- `query defs <ns>`
|
|
11
|
+
- 对带 schema 的定义添加 `[schema]` 提示,便于快速识别迁移覆盖率。
|
|
12
|
+
|
|
13
|
+
- `query def <ns/def>`
|
|
14
|
+
- 新增 `Schema` 区块输出(无 schema 时显示 `(none)`)。
|
|
15
|
+
- `--json` 输出从仅 code 扩展为完整 `CodeEntry` 结构:
|
|
16
|
+
- `doc`
|
|
17
|
+
- `examples`
|
|
18
|
+
- `code`
|
|
19
|
+
- `schema`
|
|
20
|
+
|
|
21
|
+
- `query peek <ns/def>`
|
|
22
|
+
- 新增 schema 预览(one-liner,超长截断)。
|
|
23
|
+
|
|
24
|
+
- `query find <symbol>`
|
|
25
|
+
- 引用搜索范围从 `code` 扩展到 `schema`。
|
|
26
|
+
- 输出中标注命中来源 `[code]` / `[schema]`。
|
|
27
|
+
|
|
28
|
+
- `query usages <ns/def>`
|
|
29
|
+
- 用法搜索范围从 `code` 扩展到 `schema`。
|
|
30
|
+
- 输出中标注命中来源 `[code]` / `[schema]`。
|
|
31
|
+
|
|
32
|
+
## 兼容性说明
|
|
33
|
+
|
|
34
|
+
- 未新增 CLI 参数,保持原命令兼容;schema 能力默认生效。
|
|
35
|
+
- 仅增强查询展示与搜索语义,不影响运行时求值逻辑。
|
|
36
|
+
|
|
37
|
+
## 验证
|
|
38
|
+
|
|
39
|
+
- 已执行并通过:
|
|
40
|
+
- `cargo fmt`
|
|
41
|
+
- `cargo test`
|
|
42
|
+
- `yarn check-all`
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 2026-03-06 01:20 Schema Hint Migration Batch
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
本次集中推进 `hint-fn` 新 schema map 语法迁移,覆盖 core 与测试快照中的多批函数/示例;并持续以 `cargo run --bin cr -- calcit/test.cirru -1` 与 `yarn check-all` 做回归验证。
|
|
6
|
+
|
|
7
|
+
## 关键变更点
|
|
8
|
+
|
|
9
|
+
- 在 `src/cirru/calcit-core.cirru` 中将大量旧写法 `hint-fn $ return-type ...` 迁移为:
|
|
10
|
+
- `hint-fn $ {}`
|
|
11
|
+
- `:args` / `:rest` / `:return` / `:generics` 等字段显式表达
|
|
12
|
+
- 对已由 `:args`/`:rest` 覆盖的参数类型,移除冗余 `assert-type`(保持必要运行时校验逻辑不变)。
|
|
13
|
+
- 同步修正若干 schema 与实现不一致处(例如部分参数应为 `:number`/`:fn` 的场景)。
|
|
14
|
+
- 完成多处嵌套局部函数(如 `%map`、`%map-indexed`、`%pairs-map`、`%select-keys`、`%repeat` 等)的 hint 迁移。
|
|
15
|
+
- 继续保持 `calcit/*.cirru` 测试快照侧的新语法一致性。
|
|
16
|
+
|
|
17
|
+
## 工具与流程
|
|
18
|
+
|
|
19
|
+
- 使用新增命令 `cr edit format` 对变更的 snapshot 文件做规范化重写:
|
|
20
|
+
- `src/cirru/calcit-core.cirru`
|
|
21
|
+
- `calcit/test-generics.cirru`
|
|
22
|
+
- `calcit/test-js.cirru`
|
|
23
|
+
- `calcit/test-types-inference.cirru`
|
|
24
|
+
- `calcit/test-types.cirru`
|
|
25
|
+
- 使用 `cargo fmt` 统一 Rust 代码格式。
|
|
26
|
+
|
|
27
|
+
## 验证
|
|
28
|
+
|
|
29
|
+
- 多轮执行并通过:
|
|
30
|
+
- `cargo run --bin cr -- calcit/test.cirru -1`
|
|
31
|
+
- `yarn check-all`
|
|
32
|
+
- 迁移过程中持续统计 `src/cirru/calcit-core.cirru` 内剩余旧模式数量,确认总体下降趋势。
|
|
33
|
+
|
|
34
|
+
## 经验
|
|
35
|
+
|
|
36
|
+
- 在 schema-first 迁移中,优先迁移“签名简单、类型清晰”的函数可快速稳定推进。
|
|
37
|
+
- 对复杂逻辑函数保留运行时断言,但避免与 `:args` 重复声明同一层参数类型。
|
|
38
|
+
- 每批次后立即执行 targeted + full 验证,能快速定位回归并降低累计风险。
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## 概要
|
|
2
|
+
|
|
3
|
+
本次提交继续推进 schema-first 迁移,聚焦 `src/cirru/calcit-core.cirru`,为核心宏入口补齐显式 `:schema`,在不改变运行逻辑的前提下提升静态信息覆盖率与一致性。
|
|
4
|
+
|
|
5
|
+
## 关键改动
|
|
6
|
+
|
|
7
|
+
- 补齐高频线程/控制宏 schema:`->`、`->>`、`<-`、`if-not`、`thread-as`、`thread-first`、`thread-last`、`;nil`、`:`。
|
|
8
|
+
- 补齐构造/模式宏 schema:`list-match`、`record-match`、`record-with`、`tag-match`、`field-match`、`{}`。
|
|
9
|
+
- 补齐内部宏入口 schema:`&case`、`&list-match-internal`、`&record-match-internal`、`&field-match-internal`。
|
|
10
|
+
- 补齐工具/调试宏 schema:`[,]`、`[][]`、`\`、`\.`、`call-w-log`、`call-wo-log`、`js-object`、`w-log`、`w-js-log`、`with-cpu-time`、`with-gensyms`、`wo-log`、`wo-js-log`、`noted`。
|
|
11
|
+
|
|
12
|
+
## 结果
|
|
13
|
+
|
|
14
|
+
- `calcit-core` 中 `defmacro` 对应的 `:schema nil` 已清空。
|
|
15
|
+
- 每批改动后均执行 `edit format` 与 `yarn check-all`,回归通过。
|
|
16
|
+
|
|
17
|
+
## 经验
|
|
18
|
+
|
|
19
|
+
- 采用“按语义分组小批次补齐 + 每批全量回归”策略,可在大文件中稳定推进迁移并快速定位异常。
|
|
20
|
+
- 对宏统一使用 `:kind :macro` 与 `:args/:rest/:return` 结构,能减少后续工具链处理分支。
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 2026-03-06 15:52 core schema + hint-fn 迁移批次
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
本批次延续 schema-first 迁移:
|
|
6
|
+
|
|
7
|
+
- 在 `src/cirru/calcit-core.cirru` 继续将 runtime 内建的 `:schema nil` 改为显式 schema。
|
|
8
|
+
- 在 `calcit/` 测试目录中,将旧式 `hint-fn` 参数写法迁移为新写法。
|
|
9
|
+
|
|
10
|
+
## 主要改动
|
|
11
|
+
|
|
12
|
+
### 1) Core runtime schema 补齐(无行为改动)
|
|
13
|
+
|
|
14
|
+
在 `src/cirru/calcit-core.cirru` 增补了多批低风险条目的 schema,覆盖例如:
|
|
15
|
+
|
|
16
|
+
- 数学/比较/位运算:`cos`/`sin`/`sqrt`/`round`/`round?`/`floor`/`ceil`、`bit-*` 系列等
|
|
17
|
+
- 字符串/解析/格式化:`split`/`split-lines`/`trim`/`parse-cirru*`/`format-cirru*`
|
|
18
|
+
- IO 与环境:`read-file`/`write-file`/`get-env`/`generate-id!`
|
|
19
|
+
- 运行时工具:`atom`/`add-watch`/`remove-watch`/`type-of`/`turn-*`/`fold*`/`range`/`recur`/`raise`/`quit!`
|
|
20
|
+
|
|
21
|
+
原则:仅补 schema,不改运行时逻辑。
|
|
22
|
+
|
|
23
|
+
### 2) 旧式 hint-fn 参数写法迁移
|
|
24
|
+
|
|
25
|
+
在 `calcit/test-types-inference.cirru` 中,将旧写法:
|
|
26
|
+
|
|
27
|
+
- `:args $ [] :number`
|
|
28
|
+
|
|
29
|
+
迁移为新写法:
|
|
30
|
+
|
|
31
|
+
- `:args $ [] (:: 'x :number)`
|
|
32
|
+
|
|
33
|
+
并复查 `calcit/**` 下同模式,不再命中。
|
|
34
|
+
|
|
35
|
+
## 验证
|
|
36
|
+
|
|
37
|
+
每批次均执行:
|
|
38
|
+
|
|
39
|
+
- `cargo run --bin cr -- demos/compact.cirru edit format`
|
|
40
|
+
- `yarn check-all`
|
|
41
|
+
|
|
42
|
+
结果均通过(尾部稳定为 `... and 24 files not changed.`)。
|
|
43
|
+
|
|
44
|
+
## 备注
|
|
45
|
+
|
|
46
|
+
- 当前提交聚合了本轮连续小批次迁移结果,便于后续按文件/功能继续清理剩余 `:schema nil`。
|
|
47
|
+
|
|
48
|
+
## 增补(同批次续改)
|
|
49
|
+
|
|
50
|
+
### 3) 移除 legacy hint-fn clause 兼容(改为直接报错)
|
|
51
|
+
|
|
52
|
+
在 `src/runner/preprocess.rs` 中将旧语法兼容从 warning 升级为 hard error:
|
|
53
|
+
|
|
54
|
+
- 不再接受 `hint-fn` 内 legacy clauses:`return-type` / `generics` / `type-vars`
|
|
55
|
+
- 统一在 `preprocess_hint_fn` 阶段报 `Syntax` 错误,提示迁移到 schema map 形式
|
|
56
|
+
- 删除旧的 `warn_on_legacy_hint_fn_syntax` 路径并同步测试
|
|
57
|
+
|
|
58
|
+
### 4) 文档更新(docs + guidebook)
|
|
59
|
+
|
|
60
|
+
同步更新示例与说明,避免继续传播旧写法:
|
|
61
|
+
|
|
62
|
+
- `docs/CalcitAgent.md`:补充“legacy clause 会直接报错”,并将 `:args` 示例改为命名参数条目
|
|
63
|
+
- `guidebook/docs/cirru-syntax.md`、`guidebook/docs/features/static-analysis.md`、`guidebook/docs/features/records.md`:统一 `hint-fn` 示例为 schema-map + `(:: 'arg <type>)` 形式
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# 本次修改记录
|
|
2
|
+
|
|
3
|
+
- 统一 schema 风格:清理 `:args` / `:rest` 中参数名,保留类型表达式,降低重复声明。
|
|
4
|
+
- 预处理前置 schema:在 `preprocess_defn` 中优先注入 schema hint,再进行类型与返回值检查。
|
|
5
|
+
- 程序装载增强:`program` 层为定义缓存并暴露 schema 查询能力。
|
|
6
|
+
- 快照规范化:`snapshot` 序列化时统一去除 schema 的 `:name` 字段与命名参数注解。
|
|
7
|
+
- 文档同步:更新 Agent 文档中 `hint-fn` / schema 写法示例到当前风格。
|
|
8
|
+
|
|
9
|
+
## 验证
|
|
10
|
+
|
|
11
|
+
- `cargo test` 通过。
|
|
12
|
+
- `yarn check-all` 通过。
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# 2026-03-07 migrate hint-fn to schema for top-level defs
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
`calcit-core.cirru` 中大量顶层函数在函数体内以 `hint-fn $ {} (:return ...)` 的方式标注返回类型。这是旧写法,新写法是在 `:schema` 字段中声明 `{} (:kind :fn) (:return ...)` 等结构。
|
|
6
|
+
|
|
7
|
+
## 本次操作
|
|
8
|
+
|
|
9
|
+
- 将所有顶层定义(`defn`/`defmacro`)的 `hint-fn` 从函数体 `[3,0]` 位置删除,改为 `edit schema` 写入 `:schema` 字段
|
|
10
|
+
- 共处理 100+ 个函数,包含 `calcit.core` 和 `calcit.internal` 两个 namespace
|
|
11
|
+
- 对带泛型的函数(`&list:filter`、`&list:map`、`&fn:bind` 等)补充了 `:generics` 字段
|
|
12
|
+
|
|
13
|
+
## 保留 hint-fn 的情况
|
|
14
|
+
|
|
15
|
+
内层/局部函数没有 schema 位置,仍然保留 hint-fn,例如:
|
|
16
|
+
|
|
17
|
+
- `{,}` 内的 `&{,}` 局部函数
|
|
18
|
+
- `map` 内的 `%map`
|
|
19
|
+
- `join` 内的 `%join` / `%join-str`
|
|
20
|
+
- `select-keys` 内的 `%select-keys`
|
|
21
|
+
- `&map:filter` 等内部 defn
|
|
22
|
+
|
|
23
|
+
## 修复的 bug
|
|
24
|
+
|
|
25
|
+
- `parse_target` 在 `query.rs` / `edit.rs` 中使用 `rsplit_once('/')` 导致函数名含 `/`(如 `/`、`/=`)无法识别,改为 `split_once('/')` 修复
|
|
26
|
+
|
|
27
|
+
## 验证
|
|
28
|
+
|
|
29
|
+
`yarn try-rs` 全程通过,最终耗时约 350ms。
|