@calcit/procs 0.12.20 → 0.12.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yarn/install-state.gz +0 -0
- package/README.md +10 -1
- package/editing-history/202604170520-simplify-generic-defns-via-method-dispatch.md +49 -0
- package/editing-history/202604172316-wasm-do-println-fixes.md +41 -0
- package/editing-history/202604181208-split-emit-wasm-and-bump-version.md +15 -0
- package/editing-history/202604181430-wasm-list-match-and-method-dispatch.md +5 -0
- package/editing-history/202604182041-cr-wasm-split-and-runtime-stability.md +18 -0
- package/editing-history/202604182045-v0.12.23-ci-wasm-fix.md +13 -0
- package/lib/calcit.procs.mjs +53 -0
- package/lib/package.json +2 -1
- package/package.json +2 -1
- package/rfc/04-15-wasm-compilation-feasibility.md +52 -0
- package/ts-src/calcit.procs.mts +54 -0
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
package/README.md
CHANGED
|
@@ -29,12 +29,19 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
|
29
29
|
cargo install calcit
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
4 binaries are installed:
|
|
33
33
|
|
|
34
34
|
- `calcit`, the runtime and js compiler
|
|
35
|
+
- `cr-wasm`, standalone WASM codegen tool
|
|
35
36
|
- `caps`, for downloading dependencies declared in `deps.cirru`
|
|
36
37
|
- `bundle_calcit`, bundle code if you don't want to use Calcit Editor
|
|
37
38
|
|
|
39
|
+
When installing from source, explicitly include both runners:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cargo install --path . --bin cr --bin cr-wasm --bin caps --bin bundle_calcit
|
|
43
|
+
```
|
|
44
|
+
|
|
38
45
|
To use Calcit in GitHub Actions, try [setup-cr](https://github.com/calcit-lang/setup-cr).
|
|
39
46
|
|
|
40
47
|
### Usage
|
|
@@ -163,6 +170,8 @@ cargo run --bin cr -- calcit/test.cirru -1 js && yarn try-js
|
|
|
163
170
|
cargo run --bin cr -- eval 'range 100'
|
|
164
171
|
|
|
165
172
|
cr compact.cirru -1 ir # compiles intermediate representation into program-ir.cirru
|
|
173
|
+
|
|
174
|
+
cr-wasm calcit/test-wasm.cirru # compile standalone wasm target to js-out/program.wasm
|
|
166
175
|
```
|
|
167
176
|
|
|
168
177
|
- [Cirru Parser](https://github.com/Cirru/parser.rs) for indentation-based syntax parsing.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Simplify generic core defns via `.method` dispatch
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
将 `calcit-core.cirru` 中的泛型 defn(`assoc` / `contains?` / `count` / `empty` / `empty?` / `filter` / `first` / `get` / `includes?` / `nth` / `rest` / `map`)从多分支 `if (list? x) ... if (map? x) ... if (record? x) ...` 链简化为 `list?` 快速路径 + `.method` 动态分发。
|
|
6
|
+
|
|
7
|
+
## 动机
|
|
8
|
+
|
|
9
|
+
- 用户要求:优先使用 `.method` 这套已有概念承担多态分发,避免每新增类型(record / tuple / set 等)都要在每个 defn 中追加分支。
|
|
10
|
+
- 所有 built-in 类型已在 `&core-*-methods` 中注册了对应的 `.assoc` `.count` `.empty?` `.get` `.nth` 等方法条目,编译期 `try_inline_method_call` 已能在静态类型已知时把方法调用内联为直接 proc 调用,runtime 则由 `invoke_method` 处理。这使得 defn 自身不再需要枚举类型。
|
|
11
|
+
|
|
12
|
+
## 实现
|
|
13
|
+
|
|
14
|
+
以 `empty?` 为例:
|
|
15
|
+
|
|
16
|
+
```cirru
|
|
17
|
+
defn empty? (x)
|
|
18
|
+
if (nil? x) true $ if (list? x) (&list:empty? x) (.empty? x)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
保留 `nil?` 与 `list?` 两个前置分支,其余类型全部交给 `.empty?`。
|
|
22
|
+
|
|
23
|
+
**为什么保留 `list?` 快速路径**:
|
|
24
|
+
`ensure_ns_def_compiled(CORE_NS, &init-builtin-impls!)` 在预处理期会展开 `do` 等 macro,其中 `(empty? body)` 会在 impls 还未注册到 runtime 时被调用;若此时 `empty?` 体里就走 `.empty?` → `invoke_method` → `evaluate_symbol_from_program("&core-list-impls", ...)` 会命中 quick-path 失败(循环依赖),导致 panic:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
preprocess builtin impls: CalcitErr { kind: Var, msg: "expected symbol `&core-list-impls` from path `calcit.core`, this is a quick path, should succeed", ... }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`list?` 本身只用 `(&= (type-of x) :list)` 等 proc,不依赖 impl 注册,能在 bootstrap 期安全使用,打破循环;其余类型的 methods 此时已构建完毕,可顺利走方法派发。
|
|
31
|
+
|
|
32
|
+
## 波及范围
|
|
33
|
+
|
|
34
|
+
- `src/cirru/calcit-core.cirru`:12 个 defn 的 body 瘦身。
|
|
35
|
+
- 预处理期的 `try_specialize_polymorphic_call`(已有)照常将 `(assoc x k v)` 等静态可推断调用折叠为 `&list:assoc`/`&map:assoc` 等 proc。
|
|
36
|
+
- runtime 侧未变:仍由 `invoke_method` + `&core-*-impls` 完成最终派发。
|
|
37
|
+
|
|
38
|
+
## 验证
|
|
39
|
+
|
|
40
|
+
- `cargo fmt`
|
|
41
|
+
- `cargo clippy --release -- -D warnings`
|
|
42
|
+
- `cargo test --release`:179 + 67 通过
|
|
43
|
+
- `yarn check-all`:全部 WASM/JS/解释执行测试通过
|
|
44
|
+
- recollect (`cr --entry test` + `cr --entry test js` + `node test.mjs`) ✓
|
|
45
|
+
- respo (`cr --check-only` + `cr js` + `yarn vite build`) ✓
|
|
46
|
+
|
|
47
|
+
## 备注
|
|
48
|
+
|
|
49
|
+
- 下一步可以考虑:把那些只剩 `list?` 单分支的 defn 再收拢到一个运行时 primitive,或在 preprocess 阶段对 macro 展开期调用的 `empty?` 等函数强制内联 proc,从而彻底去掉这条 fast path。
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 202604172316 — WASM `do` body 与 println host import 修复
|
|
2
|
+
|
|
3
|
+
## 修复内容
|
|
4
|
+
|
|
5
|
+
### 1. `do` 在 defn body 中的 WASM 编译问题
|
|
6
|
+
|
|
7
|
+
**现象**:`defn test-println () do (println 42) 1` 编译后生成 `f64.const 0`(函数体为空)。
|
|
8
|
+
|
|
9
|
+
**根因**:Cirru 语法中 `defn f () do expr1 expr2` 解析为 `(defn f () do expr1 expr2)`,body 数组为 `[do, expr1, expr2]` 三个独立元素(不是 `[(do expr1 expr2)]`)。`do` 作为裸 Import 节点出现在 `emit_expr` 时,触发 "unsupported WASM expression" 错误,导致函数被跳过并 fallback 为 `f64.const 0`。
|
|
10
|
+
|
|
11
|
+
**修复**:在 `emit_expr` 中为 `Calcit::Import { def: "do" }` 添加特例,将其视为 no-op(emit 0.0 后由 `emit_body` 的 Drop 消耗掉)。
|
|
12
|
+
|
|
13
|
+
```rust
|
|
14
|
+
// `do` as bare body expression is a no-op sequencer
|
|
15
|
+
Calcit::Import(import) if import.def.as_ref() == "do" => {
|
|
16
|
+
ctx.emit(f64_const(0.0));
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 2. io.log_value host import(WASM println)
|
|
21
|
+
|
|
22
|
+
- WASM codegen 对 `println`/`eprintln`/`echo` 调用 host import `io.log_value`。
|
|
23
|
+
- `test-wasm.mjs` 提供 `io.log_value` 实现:读取堆内存判断类型,字符串解码 UTF-8,数字直接输出。
|
|
24
|
+
- 添加 `test-println` 测试用例验证功能。
|
|
25
|
+
|
|
26
|
+
### 3. TypeScript 运行时 BufList 修复
|
|
27
|
+
|
|
28
|
+
`_$n_buf_list_$o_concat` 中 `CalcitSliceList` 的 `items` 是方法而非属性,需调用 `xs.items()` 并手动迭代 Generator(TS 配置不支持 `for...of` Generator)。
|
|
29
|
+
|
|
30
|
+
## 受影响文件
|
|
31
|
+
|
|
32
|
+
- `src/codegen/emit_wasm.rs`:`emit_expr` 新增 `do` no-op 特例;`emit_call_expr` Import 分支也保留 `do` 处理(用于 `(do ...)` 调用形式)
|
|
33
|
+
- `ts-src/calcit.procs.mts`:修正 `_$n_buf_list_$o_concat` 迭代逻辑
|
|
34
|
+
- `calcit/test-wasm.cirru`:新增 `test-println`
|
|
35
|
+
- `scripts/test-wasm.mjs`:新增 `io.log_value` 实现与 `test-println` 断言
|
|
36
|
+
|
|
37
|
+
## 关键经验
|
|
38
|
+
|
|
39
|
+
- `do` 在 Calcit defn body 中是语法糖/占位符,预处理后展开为多个独立 body 表达式,不是 `(do ...)` 调用形式。WASM codegen 需将其视为 no-op。
|
|
40
|
+
- WASM 编译错误时 fallback 为 `f64.const 0`(静默),调试时需检查 stderr 中的 `[wasm] skipping` 日志。
|
|
41
|
+
- TS 的 Generator 不能直接 `for...of`,需手动调用 `.next()`。
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
- Split the WASM code generator so `src/codegen/emit_wasm.rs` keeps orchestration while runtime helpers, method dispatch, and record/tuple emitters live in dedicated submodules.
|
|
4
|
+
- Bumped the Calcit patch version from `0.12.20` to `0.12.21` for the next release.
|
|
5
|
+
|
|
6
|
+
## Module Layout
|
|
7
|
+
|
|
8
|
+
- `src/codegen/emit_wasm/runtime.rs` now owns host import definitions, module assembly, and internal runtime helper builders.
|
|
9
|
+
- `src/codegen/emit_wasm/methods.rs` now owns dynamic method dispatch and rest-arg call argument packing.
|
|
10
|
+
- `src/codegen/emit_wasm/records.rs` now owns record and tuple emission helpers.
|
|
11
|
+
|
|
12
|
+
## Notes
|
|
13
|
+
|
|
14
|
+
- The split is intended to be behavior-preserving; no WASM semantics were changed as part of this refactor.
|
|
15
|
+
- Version strings were updated in `Cargo.toml`, `package.json`, and `lib/package.json`, while generated JS version text will be refreshed by the validation build.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
- WASM export names now stay unique by qualifying only colliding defs with `ns/def`.
|
|
2
|
+
- `list-match` macro now uses `&list:empty?` after the existing list guard, avoiding the unsupported generic `empty?` invoke path in WASM.
|
|
3
|
+
- Added minimal `Calcit::Method` handling in WASM for dynamic `.empty?`, `.count`, `.nth`, and `.get` calls.
|
|
4
|
+
- `&tuple:nth` in WASM now accepts dynamic indices instead of literal-only indices.
|
|
5
|
+
- Verified `yarn try-all` still passes in calcit after the WASM changes.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 2026-04-18 20:41 CR WASM Split and Runtime Stability
|
|
2
|
+
|
|
3
|
+
## 关键修改
|
|
4
|
+
|
|
5
|
+
- 将 `cr wasm` 拆分为独立二进制 `cr-wasm`,避免主 `cr` 命令体积和职责继续膨胀。
|
|
6
|
+
- `calcit` 的 WASM 相关验证脚本和 CI 调整为优先走 `cr-wasm`。
|
|
7
|
+
- 修复 `emit_wasm` 中 map/set/tuple 相关回归点,恢复 `yarn check-all` 的可通过性。
|
|
8
|
+
- 修复 `recollect` 在 Rust 运行时下的 `patch-map` 路径,避免本地变量解析异常导致测试阻断。
|
|
9
|
+
- 将 `recollect` 的 wasm 测试分为核心 smoke(阻断)与扩展 probes(非阻断),保证 wasm 独立验证但不影响 rust/js 主链路。
|
|
10
|
+
- 版本提升到 patch:`0.12.22`。
|
|
11
|
+
|
|
12
|
+
## 经验与注意事项
|
|
13
|
+
|
|
14
|
+
- 对 `compact.cirru` 的修改必须通过 `cr edit`/`cr tree` 完成,避免文本直接编辑破坏结构。
|
|
15
|
+
- 在多项目联动(calcit + recollect)场景下,发布前验证至少应覆盖:
|
|
16
|
+
- calcit: `yarn check-all`
|
|
17
|
+
- recollect: `yarn test:cr`、`yarn test:js`、`yarn test:wasm`
|
|
18
|
+
- macOS 上遇到 `iconv` 链接问题时,需带 `SDKROOT` 执行构建命令进行最终回归确认。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 2026-04-18 20:45 v0.12.23 CI WASM Fix
|
|
2
|
+
|
|
3
|
+
## 变更摘要
|
|
4
|
+
|
|
5
|
+
- 解决 GitHub Actions `try wasm` 步骤失败(exit 127)的问题。
|
|
6
|
+
- 原因:workflow 直接调用 `./target/debug/cr-wasm`,但未提前构建二进制。
|
|
7
|
+
- 修复:在 `.github/workflows/test.yaml` 的 wasm 步骤中先执行 `cargo build --bin cr-wasm`,再运行 `scripts/test-wasm.sh`。
|
|
8
|
+
- 版本升级:`0.12.22` -> `0.12.23`(`Cargo.toml` 与 `package.json` 同步)。
|
|
9
|
+
|
|
10
|
+
## 验证
|
|
11
|
+
|
|
12
|
+
- 本地 `cargo check` 通过。
|
|
13
|
+
- 后续以推送后的 Actions `Test` 工作流成功作为发布门禁,再创建 GitHub Release。
|
package/lib/calcit.procs.mjs
CHANGED
|
@@ -283,6 +283,56 @@ export function _$n_record_$o_count(x) {
|
|
|
283
283
|
return x.fields.length;
|
|
284
284
|
throw new Error(`expected a record ${x}`);
|
|
285
285
|
}
|
|
286
|
+
export function _$n_record_$o_field_tag(x, idx) {
|
|
287
|
+
if (!(x instanceof CalcitRecord))
|
|
288
|
+
throw new Error(`&record:field-tag expected a record, got ${x}`);
|
|
289
|
+
const i = idx;
|
|
290
|
+
if (i < 0 || i >= x.fields.length)
|
|
291
|
+
throw new Error(`&record:field-tag index ${i} out of bounds (${x.fields.length})`);
|
|
292
|
+
return x.fields[i];
|
|
293
|
+
}
|
|
294
|
+
// === BufList — mutable append-only list ===
|
|
295
|
+
// Wrapper around a plain JS Array for O(1) push.
|
|
296
|
+
export class CalcitBufList {
|
|
297
|
+
constructor(buf) {
|
|
298
|
+
this.buf = buf ?? [];
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
export function _$n_buf_list_$o_new() {
|
|
302
|
+
return new CalcitBufList();
|
|
303
|
+
}
|
|
304
|
+
export function _$n_buf_list_$o_push(buf, item) {
|
|
305
|
+
if (!(buf instanceof CalcitBufList))
|
|
306
|
+
throw new Error(`&buf-list:push expected a buf-list, got ${buf}`);
|
|
307
|
+
buf.buf.push(item);
|
|
308
|
+
return buf;
|
|
309
|
+
}
|
|
310
|
+
export function _$n_buf_list_$o_concat(buf, xs) {
|
|
311
|
+
if (!(buf instanceof CalcitBufList))
|
|
312
|
+
throw new Error(`&buf-list:concat expected a buf-list, got ${buf}`);
|
|
313
|
+
if (xs instanceof CalcitSliceList || xs instanceof CalcitList) {
|
|
314
|
+
const gen = xs.items();
|
|
315
|
+
let next = gen.next();
|
|
316
|
+
while (!next.done) {
|
|
317
|
+
buf.buf.push(next.value);
|
|
318
|
+
next = gen.next();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
throw new Error(`&buf-list:concat expected a list, got ${xs}`);
|
|
323
|
+
}
|
|
324
|
+
return buf;
|
|
325
|
+
}
|
|
326
|
+
export function _$n_buf_list_$o_to_list(buf) {
|
|
327
|
+
if (!(buf instanceof CalcitBufList))
|
|
328
|
+
throw new Error(`&buf-list:to-list expected a buf-list, got ${buf}`);
|
|
329
|
+
return new CalcitSliceList([...buf.buf]);
|
|
330
|
+
}
|
|
331
|
+
export function _$n_buf_list_$o_count(buf) {
|
|
332
|
+
if (!(buf instanceof CalcitBufList))
|
|
333
|
+
throw new Error(`&buf-list:count expected a buf-list, got ${buf}`);
|
|
334
|
+
return buf.buf.length;
|
|
335
|
+
}
|
|
286
336
|
export function _$n_set_$o_count(x) {
|
|
287
337
|
if (x instanceof CalcitSet)
|
|
288
338
|
return x.len();
|
|
@@ -1387,6 +1437,9 @@ export let nil_$q_ = (x) => {
|
|
|
1387
1437
|
export let tag_$q_ = (x) => {
|
|
1388
1438
|
return x instanceof CalcitTag;
|
|
1389
1439
|
};
|
|
1440
|
+
export let symbol_$q_ = (x) => {
|
|
1441
|
+
return x instanceof CalcitSymbol;
|
|
1442
|
+
};
|
|
1390
1443
|
export let map_$q_ = (x) => {
|
|
1391
1444
|
return x instanceof CalcitSliceMap || x instanceof CalcitMap;
|
|
1392
1445
|
};
|
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calcit/procs",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.23",
|
|
4
4
|
"main": "./lib/calcit.procs.mjs",
|
|
5
5
|
"devDependencies": {
|
|
6
6
|
"@types/node": "^25.0.9",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"bench-recur-smoke": "cargo run --bin cr -- calcit/test.cirru 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));\"",
|
|
20
20
|
"check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
|
|
21
21
|
"check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
|
|
22
|
+
"try-all": "yarn check-all",
|
|
22
23
|
"try-rs": "cargo run --bin cr -- calcit/test.cirru",
|
|
23
24
|
"warn-dyn-method": "cargo run --bin cr -- calcit/test.cirru --warn-dyn-method",
|
|
24
25
|
"try-js-brk": "cargo run --bin cr -- calcit/test.cirru js && node --inspect-brk js-out/main.mjs",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calcit/procs",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.23",
|
|
4
4
|
"main": "./lib/calcit.procs.mjs",
|
|
5
5
|
"devDependencies": {
|
|
6
6
|
"@types/node": "^25.0.9",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"bench-recur-smoke": "cargo run --bin cr -- calcit/test.cirru 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));\"",
|
|
20
20
|
"check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
|
|
21
21
|
"check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
|
|
22
|
+
"try-all": "yarn check-all",
|
|
22
23
|
"try-rs": "cargo run --bin cr -- calcit/test.cirru",
|
|
23
24
|
"warn-dyn-method": "cargo run --bin cr -- calcit/test.cirru --warn-dyn-method",
|
|
24
25
|
"try-js-brk": "cargo run --bin cr -- calcit/test.cirru js && node --inspect-brk js-out/main.mjs",
|
|
@@ -222,6 +222,58 @@ WASM GC proposal(2024 年起 V8/SpiderMonkey 已发布)提供:
|
|
|
222
222
|
2. **中期**: 路径二的子集 — 从纯数值计算函数开始,生成 WASM 模块作为 JS codegen 的"加速岛"(hot island),由 JS 运行时按需调用
|
|
223
223
|
3. **远期**: 路径三 — 随 WASM GC 工具链成熟,逐步扩展可编译子集
|
|
224
224
|
|
|
225
|
+
## 2026-04 进展状态
|
|
226
|
+
|
|
227
|
+
当前项目已经不再停留在“可行性评估”阶段,而是进入“持续补齐子集能力”的实施阶段:
|
|
228
|
+
|
|
229
|
+
- `src/codegen/emit_wasm.rs` 已落地,并拆分出 `runtime.rs`、`methods.rs`、`records.rs` 子模块维护。
|
|
230
|
+
- `yarn check-all` 已纳入 WASM 验证,当前会编译 `calcit/test-wasm.cirru` 并通过 `scripts/test-wasm.mjs` 做 Node.js 侧断言。
|
|
231
|
+
- 发布版本 `0.12.21` 已覆盖 BufList、基础 map/set/list/tuple/record 操作、若干动态方法分派、host println/logging 等运行时能力。
|
|
232
|
+
- `recollect` 已可在已发布的 `setup-cr` 工具链上启用 WASM 回归测试,但仍有更高层 API 与应用侧依赖未覆盖。
|
|
233
|
+
|
|
234
|
+
可以更直白地估计当前距离:
|
|
235
|
+
|
|
236
|
+
- 距离“底层运行时对子集集合操作足够稳固”大约还剩 `30%`。
|
|
237
|
+
- 距离“`calcit.core` 高频 helper 在真实下游项目里不需要大面积绕开”大约还剩 `50%`。
|
|
238
|
+
- 距离“Respo/recollect 这类应用侧流程可稳定跑通”大约还剩 `75%`。
|
|
239
|
+
|
|
240
|
+
这意味着近期重点不再是“证明能不能做”,而是“持续缩小尚未支持的语言与运行时边界”。
|
|
241
|
+
|
|
242
|
+
## 近期计划细化
|
|
243
|
+
|
|
244
|
+
### P0: 消除被下游真实项目阻塞的运行时缺口
|
|
245
|
+
|
|
246
|
+
优先级依据不是抽象完整性,而是下游项目是否真实卡住:
|
|
247
|
+
|
|
248
|
+
1. 继续补动态 method/runtime 缺口。
|
|
249
|
+
- `.contains?`、`.includes?`、`.empty` 已开始在 direct method path 上落地,但 `calcit.core` wrapper 侧仍有 skip 项,需要继续把 lowering 对齐到底。
|
|
250
|
+
- 当前仍缺 `.min`、`.max` 等集合方法的稳定支持。
|
|
251
|
+
- 这些能力会直接影响 `calcit.core` 高层 helper 在 WASM 下的可用性。
|
|
252
|
+
|
|
253
|
+
2. 继续补充 `calcit.core` 常见高阶路径。
|
|
254
|
+
- `foldl`、`foldl-shortcut`、`foldr-shortcut`、`sort`、`&call-spread` 仍是大量跳过项的源头。
|
|
255
|
+
- 这部分不是为了追求“理论完备”,而是为了减少真实项目中被迫绕开 core helper 的情况。
|
|
256
|
+
|
|
257
|
+
3. 持续扩大 `test-wasm.cirru` 的下游回归覆盖。
|
|
258
|
+
- 每补一个能力,都应优先加一个最小但能锁定语义边界的 WASM 用例。
|
|
259
|
+
- 需要优先覆盖下游真实依赖的模式,而不是只补纯数值 demo。
|
|
260
|
+
|
|
261
|
+
### P1: 以 recollect 为代表补 API 级验证
|
|
262
|
+
|
|
263
|
+
`recollect` 已经证明当前 WASM 子集可支撑一部分 diff/patch 逻辑,因此下一阶段应显式面向下游 API:
|
|
264
|
+
|
|
265
|
+
1. 用 API parity fixtures 覆盖 `diff-twig` / `patch-twig`。
|
|
266
|
+
2. 增加 record-heavy / map-heavy / nested tree 场景。
|
|
267
|
+
3. 把“当前可运行”和“仍 blocked”的场景分开记录,避免 roadmap 只剩模糊叙述。
|
|
268
|
+
|
|
269
|
+
### P2: 推进解释器到 wasm32 的基础编译检查
|
|
270
|
+
|
|
271
|
+
路径一仍值得保留,但它的短期目标应更具体:
|
|
272
|
+
|
|
273
|
+
1. 先跑通 `cargo build --target wasm32-unknown-unknown --lib`。
|
|
274
|
+
2. 把剩余链接错误收敛成明确的 `cfg` 门控清单。
|
|
275
|
+
3. 把解释器路径与 AOT 路径共享的可复用运行时能力整理出来,避免两条线重复补洞。
|
|
276
|
+
|
|
225
277
|
## 验证第一步
|
|
226
278
|
|
|
227
279
|
```bash
|
package/ts-src/calcit.procs.mts
CHANGED
|
@@ -316,6 +316,57 @@ export function _$n_record_$o_count(x: CalcitValue): number {
|
|
|
316
316
|
|
|
317
317
|
throw new Error(`expected a record ${x}`);
|
|
318
318
|
}
|
|
319
|
+
|
|
320
|
+
export function _$n_record_$o_field_tag(x: CalcitValue, idx: CalcitValue): CalcitTag {
|
|
321
|
+
if (!(x instanceof CalcitRecord)) throw new Error(`&record:field-tag expected a record, got ${x}`);
|
|
322
|
+
const i = idx as number;
|
|
323
|
+
if (i < 0 || i >= x.fields.length) throw new Error(`&record:field-tag index ${i} out of bounds (${x.fields.length})`);
|
|
324
|
+
return x.fields[i];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// === BufList — mutable append-only list ===
|
|
328
|
+
// Wrapper around a plain JS Array for O(1) push.
|
|
329
|
+
export class CalcitBufList {
|
|
330
|
+
buf: CalcitValue[];
|
|
331
|
+
constructor(buf?: CalcitValue[]) {
|
|
332
|
+
this.buf = buf ?? [];
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function _$n_buf_list_$o_new(): CalcitBufList {
|
|
337
|
+
return new CalcitBufList();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function _$n_buf_list_$o_push(buf: CalcitValue, item: CalcitValue): CalcitBufList {
|
|
341
|
+
if (!(buf instanceof CalcitBufList)) throw new Error(`&buf-list:push expected a buf-list, got ${buf}`);
|
|
342
|
+
buf.buf.push(item);
|
|
343
|
+
return buf;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function _$n_buf_list_$o_concat(buf: CalcitValue, xs: CalcitValue): CalcitBufList {
|
|
347
|
+
if (!(buf instanceof CalcitBufList)) throw new Error(`&buf-list:concat expected a buf-list, got ${buf}`);
|
|
348
|
+
if (xs instanceof CalcitSliceList || xs instanceof CalcitList) {
|
|
349
|
+
const gen = xs.items();
|
|
350
|
+
let next = gen.next();
|
|
351
|
+
while (!next.done) {
|
|
352
|
+
buf.buf.push(next.value);
|
|
353
|
+
next = gen.next();
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
throw new Error(`&buf-list:concat expected a list, got ${xs}`);
|
|
357
|
+
}
|
|
358
|
+
return buf;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function _$n_buf_list_$o_to_list(buf: CalcitValue): CalcitSliceList {
|
|
362
|
+
if (!(buf instanceof CalcitBufList)) throw new Error(`&buf-list:to-list expected a buf-list, got ${buf}`);
|
|
363
|
+
return new CalcitSliceList([...buf.buf]);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function _$n_buf_list_$o_count(buf: CalcitValue): number {
|
|
367
|
+
if (!(buf instanceof CalcitBufList)) throw new Error(`&buf-list:count expected a buf-list, got ${buf}`);
|
|
368
|
+
return buf.buf.length;
|
|
369
|
+
}
|
|
319
370
|
export function _$n_set_$o_count(x: CalcitValue): number {
|
|
320
371
|
if (x instanceof CalcitSet) return x.len();
|
|
321
372
|
|
|
@@ -1504,6 +1555,9 @@ export let nil_$q_ = (x: CalcitValue): boolean => {
|
|
|
1504
1555
|
export let tag_$q_ = (x: CalcitValue): boolean => {
|
|
1505
1556
|
return x instanceof CalcitTag;
|
|
1506
1557
|
};
|
|
1558
|
+
export let symbol_$q_ = (x: CalcitValue): boolean => {
|
|
1559
|
+
return x instanceof CalcitSymbol;
|
|
1560
|
+
};
|
|
1507
1561
|
export let map_$q_ = (x: CalcitValue): boolean => {
|
|
1508
1562
|
return x instanceof CalcitSliceMap || x instanceof CalcitMap;
|
|
1509
1563
|
};
|