@calcit/procs 0.12.25 → 0.12.27
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/editing-history/2026-0422-0114-wasm-hof-intercepts-and-set-intersection.md +86 -0
- package/editing-history/2026-0422-0136-wasm-enum-tuple-bool-str-fixes.md +73 -0
- package/editing-history/202604211818-wasm-string-ops-v2.md +57 -0
- package/editing-history/202604212332-split-emit-wasm.md +45 -0
- package/editing-history/202604221340-wasm-map-diff-new-and-let-intercept.md +53 -0
- package/editing-history/202604231003-wasm-improvements.md +38 -0
- package/editing-history/202604231419-v0.12.26-wasm-bisection-fixes.md +7 -0
- package/lib/package.json +1 -1
- package/package.json +1 -1
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# WASM HOF Intercepts & Set Intersection
|
|
2
|
+
|
|
3
|
+
## 修改概要
|
|
4
|
+
|
|
5
|
+
将 WASM codegen 的不支持 proc 数量从 72 减少到 68(减少 4 个)。
|
|
6
|
+
|
|
7
|
+
## 核心问题
|
|
8
|
+
|
|
9
|
+
calcit.core 内部定义了 `map/filter/each/any?/every?/find/find-index` 等 HOF,其实现体使用 `f` 参数作为调用头——在 WASM codegen 中无法静态解析局部变量作为调用目标。解决方案:在**调用点拦截**(call-site intercept),在 `emit_call_expr` 中直接 emit 内联循环。
|
|
10
|
+
|
|
11
|
+
## 文件修改
|
|
12
|
+
|
|
13
|
+
### `src/codegen/emit_wasm/hof.rs`
|
|
14
|
+
|
|
15
|
+
新增以下函数(通过 `cat >>` 追加到文件末尾):
|
|
16
|
+
|
|
17
|
+
- **`emit_unary_step`** — 对单参数 HOF callee 发出调用(支持 Static/Inline/Proc 三种 callee 类型)
|
|
18
|
+
- **`emit_binary_step_ei`** — 对 `(elem, idx)` 双参数 callee 发出调用(用于 map-indexed)
|
|
19
|
+
- **`resolve_unary_callee`** — 解析单参数 HOF callee,返回 `FoldlCallKind`
|
|
20
|
+
- **`emit_map`** — `map xs f`:对每个元素调用 f,返回等长新列表
|
|
21
|
+
- **`emit_map_indexed`** — `map-indexed xs f`:对每个 (elem, idx) 调用 f
|
|
22
|
+
- **`emit_each`** — `each xs f`:对每个元素调用 f(副作用),返回 nil
|
|
23
|
+
- **`emit_filter`** — `filter xs f`:过滤满足 f 的元素,返回新列表
|
|
24
|
+
- **`emit_any`** — `any? xs f`:任意元素满足 f 返回 1.0,否则 0.0
|
|
25
|
+
- **`emit_every`** — `every? xs f`:全部元素满足 f 返回 1.0,否则 0.0
|
|
26
|
+
- **`emit_find`** — `find xs f`:返回第一个满足 f 的元素,否则 nil
|
|
27
|
+
- **`emit_find_index`** — `find-index xs f`:返回第一个满足 f 的元素索引,否则 -1.0
|
|
28
|
+
|
|
29
|
+
### `src/codegen/emit_wasm/sets.rs`
|
|
30
|
+
|
|
31
|
+
新增:
|
|
32
|
+
|
|
33
|
+
- **`emit_set_intersection`** — `&set:intersection a b`:返回两集合共有元素。逻辑与 `emit_set_difference` 对称,条件取反(`I32Ne` 代替 `I32Eq`)。
|
|
34
|
+
|
|
35
|
+
### `src/codegen/emit_wasm/lists.rs`
|
|
36
|
+
|
|
37
|
+
修改 **`emit_list_concat`**:从只支持 2-arg 改为支持任意数量 args(通过左折叠依次合并)。新增私有辅助函数 **`emit_list_concat_two`** 处理 2-arg 快速路径。
|
|
38
|
+
|
|
39
|
+
### `src/codegen/emit_wasm.rs`
|
|
40
|
+
|
|
41
|
+
**`emit_call_expr` 的 `calcit.core` 拦截块** 新增:
|
|
42
|
+
|
|
43
|
+
```rust
|
|
44
|
+
"map" if args_list.len() == 2 => return emit_map(ctx, &args_list),
|
|
45
|
+
"map-indexed" if args_list.len() == 2 => return emit_map_indexed(ctx, &args_list),
|
|
46
|
+
"each" if args_list.len() == 2 => return emit_each(ctx, &args_list),
|
|
47
|
+
"filter" if args_list.len() == 2 => return emit_filter(ctx, &args_list),
|
|
48
|
+
"any?" if args_list.len() == 2 => return emit_any(ctx, &args_list),
|
|
49
|
+
"every?" if args_list.len() == 2 => return emit_every(ctx, &args_list),
|
|
50
|
+
"find" if args_list.len() == 2 => return emit_find(ctx, &args_list),
|
|
51
|
+
"find-index" if args_list.len() == 2 => return emit_find_index(ctx, &args_list),
|
|
52
|
+
"concat" if args_list.len() >= 2 => return emit_list_concat(ctx, &args_list),
|
|
53
|
+
"deref" if args_list.len() == 1 => return emit_expr(ctx, &args_list[0]),
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**`emit_proc_call`** 新增:
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
CalcitProc::NativeSetIntersection => emit_set_intersection(ctx, args),
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**`emit_call_spread`** 新增 `Calcit::Proc` arm:
|
|
63
|
+
|
|
64
|
+
```rust
|
|
65
|
+
Calcit::Proc(proc) => emit_call_spread_args_as_regular(ctx, proc, call_args),
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
新增辅助函数 **`emit_call_spread_args_as_regular`**:收集 spread 调用中的真实参数,跳过 ArgSpread 标记,转交 `emit_proc_call` 处理。
|
|
69
|
+
|
|
70
|
+
## 关键设计理解
|
|
71
|
+
|
|
72
|
+
### 调用点拦截 vs 定义编译
|
|
73
|
+
|
|
74
|
+
- calcit.core 的 HOF 定义(如 `calcit.core/map`)内部使用 `f` 参数作为调用头,**定义本身无法编译**(会 skip)。
|
|
75
|
+
- 拦截在 `emit_call_expr` 的 `Calcit::Import` arm,当用户代码调用 `(map my-list my-fn)` 时触发,直接 emit 内联循环,**无需编译 calcit.core/map 的定义体**。
|
|
76
|
+
- 因此 skip 列表中仍可见 `calcit.core/map`(定义 skip),但用户代码的调用点正常生成 WASM。
|
|
77
|
+
|
|
78
|
+
### 剩余 68 个 skip 的分布
|
|
79
|
+
|
|
80
|
+
- **核心库定义失败**(~66 个):使用运行时 spread(`& args`)、方法调用(`.deref`)、嵌套 defn、'f' 作为调用头等无法在 WASM 编译时静态解析的特性。
|
|
81
|
+
- **用户代码 skip**(2 个):`recollect.app.comp.panel/on-click` 和 `recollect.test/test-diff-funcs`,均因内部使用了嵌套 defn(nested lambda)。
|
|
82
|
+
|
|
83
|
+
## 测试结果
|
|
84
|
+
|
|
85
|
+
- `yarn test:wasm`:**全部通过**(=== Recollect WASM checks passed ===)
|
|
86
|
+
- skip 数量:72 → 68(减少 4 个)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# WASM Codegen: Enum Tuple, Bool Tag, and Str Fixes
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-04-22 01:36
|
|
4
|
+
**Skip count**: 68 → 62 (6 fewer)
|
|
5
|
+
|
|
6
|
+
## Changes Made
|
|
7
|
+
|
|
8
|
+
### 1. `NativeEnumTupleNew` (`%::`) — New Dedicated Emitter
|
|
9
|
+
|
|
10
|
+
**File**: `src/codegen/emit_wasm/records.rs`, `src/codegen/emit_wasm.rs`
|
|
11
|
+
|
|
12
|
+
Added `emit_enum_tuple_new` for the `%::` proc (enum variant constructor).
|
|
13
|
+
|
|
14
|
+
Key insight: `%::` always has the form `(%:: enum_class tag payload...)` where:
|
|
15
|
+
|
|
16
|
+
- `enum_class` (args[0]) is for type-checking only — **ignored in WASM**
|
|
17
|
+
- `tag` (args[1]) is the actual variant tag (Calcit::Tag)
|
|
18
|
+
- `payload` (args[2..]) are the values
|
|
19
|
+
|
|
20
|
+
This is distinct from `::` (`NativeTuple`) which uses `args[0]` as the tag directly.
|
|
21
|
+
|
|
22
|
+
**Fixed functions**: `%ok`, `%err`, `%some`, `%none` — now compile correctly.
|
|
23
|
+
|
|
24
|
+
Memory layout is identical to `::` tuples: `[count:f64][tag_id:f64][payload...]`.
|
|
25
|
+
|
|
26
|
+
### 2. Bool Support in `emit_tuple_new`
|
|
27
|
+
|
|
28
|
+
**File**: `src/codegen/emit_wasm/records.rs`
|
|
29
|
+
|
|
30
|
+
Extended `emit_tuple_new` to accept `Calcit::Bool` as the tag field:
|
|
31
|
+
|
|
32
|
+
- `true` → `1.0`
|
|
33
|
+
- `false` → `0.0`
|
|
34
|
+
|
|
35
|
+
**Purpose**: The `foldl-shortcut` pattern uses `(:: true value)` and `(:: false value)` as tagged pairs for early-exit signaling. The `foldl-shortcut` implementation reads the tag at offset +8 and compares with `1.0`.
|
|
36
|
+
|
|
37
|
+
**Fixed functions**: `calcit.core/index-of`, `calcit.core/&list:last-index-of` — these use `foldl-shortcut` with an inline `%index-of` defn that calls `(:: true idx)` / `(:: false ...)`.
|
|
38
|
+
|
|
39
|
+
### 3. Variadic `str` / `str-spaced` Call-Site Intercepts
|
|
40
|
+
|
|
41
|
+
**File**: `src/codegen/emit_wasm/strings.rs`, `src/codegen/emit_wasm.rs`
|
|
42
|
+
|
|
43
|
+
Added two new pub functions:
|
|
44
|
+
|
|
45
|
+
- `emit_str_variadic(ctx, args)`: `(str a b c ...)` → left-fold of string concat
|
|
46
|
+
- `emit_str_spaced(ctx, args)`: `(str-spaced a b c ...)` → join with space separator
|
|
47
|
+
|
|
48
|
+
Private helper:
|
|
49
|
+
|
|
50
|
+
- `concat_two_i32_ptrs(ctx, ptr_a, ptr_b)`: concatenates two strings from i32 ptr locals
|
|
51
|
+
|
|
52
|
+
**Key technique**: `emit_turn_string` converts any f64 value to a string, then `concat_two_i32_ptrs` combines them without re-emitting expressions.
|
|
53
|
+
|
|
54
|
+
For `str-spaced`, the space character is allocated dynamically (1-byte string with 0x20 written via `I32Store8`) to avoid depending on the string pool.
|
|
55
|
+
|
|
56
|
+
**Intercept added in `emit_call_expr`** for `calcit.core` namespace:
|
|
57
|
+
|
|
58
|
+
```rust
|
|
59
|
+
"str" if !args_list.is_empty() => return emit_str_variadic(ctx, &args_list),
|
|
60
|
+
"str-spaced" if !args_list.is_empty() => return emit_str_spaced(ctx, &args_list),
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
These intercepts help user code calling `str`/`str-spaced`; the core library definitions still fail because they use `(&syntax &)` variadic syntax.
|
|
64
|
+
|
|
65
|
+
## Remaining 62 Skips (Categorized)
|
|
66
|
+
|
|
67
|
+
- **14 'f callee** (HOF defs): map, filter, each, find, foldl', update, etc.
|
|
68
|
+
- **~9 nested defn**: &fn:apply, &fn:bind, &list:filter-pair, on-click, etc.
|
|
69
|
+
- **~7 complex defn callee**: group-by, frequencies, repeat, zipmap, etc.
|
|
70
|
+
- **3 `(&syntax &)`**: str, str-spaced, &str-spaced (definitions; call sites intercepted)
|
|
71
|
+
- **3 method calls**: .slice, .filter, .deref (definition level)
|
|
72
|
+
- **~4 variadic spread**: concat, mapcat, &list:flatten, &list:apply
|
|
73
|
+
- **Others**: dissoc, conj, contains-in?, tagging-edn, impl-traits, etc.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# WASM String Ops 扩展 v2
|
|
2
|
+
|
|
3
|
+
## 本次修改概要
|
|
4
|
+
|
|
5
|
+
继上一轮字符串实现(count/nth/first/rest/slice/concat/compare)之后,补充剩余实用 API。
|
|
6
|
+
|
|
7
|
+
## 新增 API
|
|
8
|
+
|
|
9
|
+
| Proc | 实现位置 | 说明 |
|
|
10
|
+
|------|----------|------|
|
|
11
|
+
| `&str:contains?` | `emit_str_contains` | 字节索引范围检查:`byte_len > idx` |
|
|
12
|
+
| `&str:find-index` | `__rt_str_find_index` (runtime.rs) | 朴素 O(n·m) 字节子串搜索,返回偏移或 -1 |
|
|
13
|
+
| `&str:includes?` | `emit_str_includes` | 调用 `find-index`,判断 `>= 0` |
|
|
14
|
+
| `&str:pad-left` | `__rt_str_pad_left` (emit_wasm.rs) | 循环填充 pattern 字节于左侧 |
|
|
15
|
+
| `&str:pad-right` | `__rt_str_pad_right` (emit_wasm.rs) | 循环填充 pattern 字节于右侧 |
|
|
16
|
+
|
|
17
|
+
## 关键实现细节
|
|
18
|
+
|
|
19
|
+
### `__rt_str_find_index`
|
|
20
|
+
|
|
21
|
+
- 放在 `runtime.rs`,不需要 `str_tag_id`(只读不分配)
|
|
22
|
+
- 使用双层嵌套 Loop/Block 结构,WASM 控制流通过 `Br(N)` 跳出多层
|
|
23
|
+
- 空 needle 返回 0.0;needle 长于 haystack 返回 -1.0
|
|
24
|
+
- `Br(5)` 从内层 If 跳出到 `$outer` Block 返回找到的索引
|
|
25
|
+
|
|
26
|
+
### `__rt_str_pad_left` / `__rt_str_pad_right`
|
|
27
|
+
|
|
28
|
+
- 放在 `emit_wasm.rs`,接受 `str_tag_id: i32` 参数(需分配 tagged 堆内存)
|
|
29
|
+
- 早退条件:`str_len >= target_size` 直接返回原指针
|
|
30
|
+
- 使用 j 变量追踪 pattern 偏移,超出 pat_len 时归零,避免 modulo 除法
|
|
31
|
+
- 注册在 `build_runtime_fns` 之后(与 `__str_new` 同一阶段)
|
|
32
|
+
|
|
33
|
+
### `emit_str_contains` 修复
|
|
34
|
+
|
|
35
|
+
初始版本使用 `I32LtU`(`byte_len < idx`)导致逻辑反转,修正为 `I32GtU`(`byte_len > idx`)。
|
|
36
|
+
|
|
37
|
+
## 测试
|
|
38
|
+
|
|
39
|
+
新增 8 个测试函数(`test-wasm.main`):
|
|
40
|
+
- `test-str-contains-true` → 1
|
|
41
|
+
- `test-str-contains-false` → 0
|
|
42
|
+
- `test-str-find-index-found` → 1("ell" 在 "hello" 偏移 1)
|
|
43
|
+
- `test-str-find-index-not-found` → -1
|
|
44
|
+
- `test-str-includes-true` → 1
|
|
45
|
+
- `test-str-includes-false` → 0
|
|
46
|
+
- `test-str-pad-left` → 5(`pad-left "hi" 5 "-"` → "---hi",count=5)
|
|
47
|
+
- `test-str-pad-right` → 5(`pad-right "hi" 5 "-"` → "hi---",count=5)
|
|
48
|
+
|
|
49
|
+
`yarn try-wasm` 全部通过(release build)。
|
|
50
|
+
|
|
51
|
+
## 文件变更
|
|
52
|
+
|
|
53
|
+
- `src/codegen/emit_wasm/runtime.rs`:新增 `build_rt_str_find_index`,在 `build_runtime_fns` 中注册
|
|
54
|
+
- `src/codegen/emit_wasm.rs`:新增 `build_str_pad_left_fn`、`build_str_pad_right_fn`、`emit_str_contains`、`emit_str_find_index`、`emit_str_includes`、`emit_str_pad_left`、`emit_str_pad_right`;修复 contains 比较方向
|
|
55
|
+
- `calcit/test-wasm.cirru`:通过 `cr edit def` 添加 8 个测试定义
|
|
56
|
+
- `scripts/test-wasm.mjs`:新增 8 个 `check()` 调用
|
|
57
|
+
- `docs/wasm-codegen.md`:更新支持表格,移除已实现项
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 202604212332 - Split emit_wasm.rs into focused submodules
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
`src/codegen/emit_wasm.rs` 原来有 6353 行,包含 WASM codegen 的所有逻辑,难以维护。
|
|
6
|
+
|
|
7
|
+
## 拆分方案
|
|
8
|
+
|
|
9
|
+
将文件按数据结构类别拆分为 6 个新子模块(位于 `src/codegen/emit_wasm/`):
|
|
10
|
+
|
|
11
|
+
| 文件 | 行数 | 内容 |
|
|
12
|
+
|------|------|------|
|
|
13
|
+
| `heap.rs` | 266 | 内存管理:bump allocator、类型标签查找、哈希辅助函数 |
|
|
14
|
+
| `lists.rs` | 1130 | 列表操作 + BufList + `emit_range` + `emit_list_distinct` |
|
|
15
|
+
| `maps.rs` | 1157 | Map 基础操作 + merge/diff + `&map:destruct` + `&merge-non-nil` |
|
|
16
|
+
| `sets.rs` | 828 | Set 操作 + `&set:destruct` |
|
|
17
|
+
| `strings.rs` | 925 | 字符串操作 + pad + `build_str_*` runtime 函数 |
|
|
18
|
+
| `hof.rs` | 423 | `resolve_callee_fn_idx`、`foldl`、`foldl-shortcut`、`foldr-shortcut` |
|
|
19
|
+
|
|
20
|
+
父文件 `emit_wasm.rs` 精简到 1656 行,保留:
|
|
21
|
+
- 模块级 doc、import、常量、基础 helpers
|
|
22
|
+
- `emit_wasm()` 公开入口函数
|
|
23
|
+
- 所有结构体和 impl(`CompiledFn`、`WasmCompileEnv`、`WasmGenCtx`)
|
|
24
|
+
- 编译管道函数(`extract_fn_parts`、`compile_fn`、`emit_body`)
|
|
25
|
+
- 表达式 emitter(`emit_expr`、`emit_call_expr`、`emit_proc_call` 等)
|
|
26
|
+
- Tag/String pool 收集
|
|
27
|
+
|
|
28
|
+
## 模块间可见性
|
|
29
|
+
|
|
30
|
+
- 各子模块顶部均有 `use super::*;`(继承父模块 imports)
|
|
31
|
+
- 所有导出函数使用 `pub(super) fn`(通过 Python 正则批量转换)
|
|
32
|
+
- 父模块通过 `pub(super) use heap::*` 将 heap 辅助函数传播给兄弟模块
|
|
33
|
+
- 父模块通过 `use lists::*; use maps::*; ...` 将各域函数引入 `emit_proc_call` 的作用域
|
|
34
|
+
|
|
35
|
+
## 实现技术
|
|
36
|
+
|
|
37
|
+
使用 Python 脚本(`scripts/split_emit_wasm.py`)自动化提取:
|
|
38
|
+
- 精确定位每段的起止行(避免孤立的 doc comment)
|
|
39
|
+
- 对非连续段(如 maps.rs 包含 4 段原文)进行拼接
|
|
40
|
+
- `make_pub_super()` 批量将 `^fn ` 替换为 `pub(super) fn`
|
|
41
|
+
|
|
42
|
+
## 验证
|
|
43
|
+
|
|
44
|
+
- `cargo check`:无错误,仅 1 个 `pub(super) use heap::*` 可见性警告(已用 `#[allow(unused_imports)]` 消除)
|
|
45
|
+
- `./target/release/cr calcit/test-record.cirru`:测试全部通过
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 202604221340 - WASM map diff-new fix & let intercept
|
|
2
|
+
|
|
3
|
+
## 问题与修复
|
|
4
|
+
|
|
5
|
+
### 1. `&map:diff-new` 参数顺序错误 (`maps.rs`)
|
|
6
|
+
|
|
7
|
+
**问题**: `emit_map_diff_new` 中 `args[0]` (b) 和 `args[1]` (a) 搞反,导致"b 中不在 a 里的条目"变成"a 中不在 b 里的条目"。结果 `patch-map` 的 map-splice 分支从来不添加新键,`recollect` API roundtrip 测试 `summary=41` 而不是预期的 44。
|
|
8
|
+
|
|
9
|
+
**修复**: 交换 a/b 的加载顺序——先加载 `args[0]` 为 `b`,`args[1]` 为 `a`,迭代 b 检查每个 key 是否在 a 中缺失。
|
|
10
|
+
|
|
11
|
+
### 2. `calcit.core/let` WASM 拦截 (`emit_wasm.rs`)
|
|
12
|
+
|
|
13
|
+
**问题**: `calcit.core/let` 以宏形式存在,正常编译会由预处理器展开为 `&let`,但作为 Import 出现在 call position 时 WASM codegen 找不到对应函数而报错。
|
|
14
|
+
|
|
15
|
+
**修复**:
|
|
16
|
+
- 在 `emit_call_expr` 的 `calcit.core` 拦截块中新增 `"let"` case
|
|
17
|
+
- 新增 `emit_let_multi` + `emit_let_pairs` 辅助函数,将 `(let ((name val)...) body...)` 格式转换为逐层绑定
|
|
18
|
+
|
|
19
|
+
### 3. `{}` 空 map 字面量 WASM 支持 (`emit_wasm.rs`)
|
|
20
|
+
|
|
21
|
+
**问题**: `{}` 作为裸表达式(非 call head)时,出现为 `calcit.core/{}` 的 Import 或 `CalcitProc::NativeMap`,WASM codegen 报 "unsupported WASM expression"。
|
|
22
|
+
|
|
23
|
+
**修复**:
|
|
24
|
+
- 在 `emit_expr` 的 `Calcit::Import` 分支加入 `def == "{}"` 的检查,调用 `emit_map_new(ctx, &[])`
|
|
25
|
+
- 在 `emit_expr` 新增 `Calcit::Proc(CalcitProc::NativeMap)` case,调用 `emit_map_new`
|
|
26
|
+
|
|
27
|
+
### 4. recollect probe 函数 Cirru 语法修复
|
|
28
|
+
|
|
29
|
+
**问题**: 7 个调试用 probe 函数缺少 `$` 运算符,导致 `let`/`&map:count`/`if` 等出现为裸表达式而非调用头:
|
|
30
|
+
- `probe-nested-*` (4 个): `let ...` → `$ let ...`
|
|
31
|
+
- `probe-*-map*` (3 个): `&map:count ...` → `$ &map:count ...`
|
|
32
|
+
- `probe-nested-changes-count`: fn 内部 `if` → `$ if`
|
|
33
|
+
|
|
34
|
+
**修复**: 用 `cr edit def --overwrite` 重写全部 7 个函数的 Cirru 语法。
|
|
35
|
+
|
|
36
|
+
## 测试结果
|
|
37
|
+
|
|
38
|
+
- `yarn run:wasm:api` (recollect): `api-roundtrip summary=44 expected=44 OK` ✓
|
|
39
|
+
- probe 函数全部通过 WASM 编译并返回正确结果:
|
|
40
|
+
`probe-empty-map=0, probe-map-count-1=1, probe-assoc-simple=2`
|
|
41
|
+
`probe-nested-bonus=3, probe-nested-count=10, probe-nested-map-count=2`
|
|
42
|
+
- WASM skip 数量: 62 → 51(recollect namespace 中全部消除,仅剩 calcit.core HOF/closure/variadic 等已知不支持项)
|
|
43
|
+
|
|
44
|
+
## 剩余已知 skip 分类 (51 个)
|
|
45
|
+
|
|
46
|
+
| 原因 | 数量 | 说明 |
|
|
47
|
+
|------|------|------|
|
|
48
|
+
| `'f` (动态调用头) | 26 | HOF 参数作函数调用,需函数表支持 |
|
|
49
|
+
| nested defn | 9 | 函数内部嵌套 defn(闭包),不支持 |
|
|
50
|
+
| `(&syntax &)` | 3 | 可变参数 `&`,str/str-spaced 已有拦截 |
|
|
51
|
+
| sort | 1 | `&list:sort-by` 需要比较函数回调 |
|
|
52
|
+
| recur arity | 1 | `conj` 内部尾递归 arity 不匹配 |
|
|
53
|
+
| 特殊 proc | 2 | tagging-edn、&core-number-impls |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# v0.12.26 WASM 改进总结
|
|
2
|
+
|
|
3
|
+
## 修改概要
|
|
4
|
+
|
|
5
|
+
### 1. 修复 `&>=` / `&<=` / `&>` / `&<` 在 WASM 中的 nil=0 碰撞问题
|
|
6
|
+
|
|
7
|
+
**问题**:Calcit WASM 运行时用 `0.0` 表示 nil,而 `calcit.core/&>=` 等内部函数在比较前会调用 `number? x`,但 `number? 0` 返回 false(因为 nil=0),导致 `assert` trap。
|
|
8
|
+
|
|
9
|
+
**修复**(`src/codegen/emit_wasm.rs`):在 `calcit.core` 导入分支拦截 `&>=`、`&<=`、`&>`、`&<`,直接 emit `F64Ge`/`F64Le`/`F64Gt`/`F64Lt` 指令,绕过 nil 检查。
|
|
10
|
+
|
|
11
|
+
### 2. 修复字符串相等性(`NativeEquals` / `Identical`)
|
|
12
|
+
|
|
13
|
+
**问题**:`NativeEquals` 使用 `F64Eq` 比较指针,两个内容相同的字符串因指针不同而判断不相等。
|
|
14
|
+
|
|
15
|
+
**修复**(`src/codegen/emit_wasm.rs`):改用 `__rt_generic_eq` 运行时函数,进行字节内容比较。
|
|
16
|
+
|
|
17
|
+
### 3. 修复 `&map:diff-new` 参数顺序
|
|
18
|
+
|
|
19
|
+
**问题**:实现代码注释写的 "b = args[0], a = args[1]",但实际语义应为 "a = args[0], b = args[1]",返回 b 中不在 a 里的条目。测试用例 `(&map:diff-new {:a 1 :b 2} {:b 3 :c 4 :d 5})` 期望 2,但因参数顺序错误返回 1。
|
|
20
|
+
|
|
21
|
+
**修复**(`src/codegen/emit_wasm/maps.rs`):调换 `a` 和 `b` 的赋值顺序,使 `a=args[0]`,`b=args[1]`(迭代 b,检查 a)。
|
|
22
|
+
|
|
23
|
+
### 4. 修复 test-wasm.mjs 字符串测试期望值
|
|
24
|
+
|
|
25
|
+
**问题**:`test-str-nth` 和 `test-str-first` 的测试期望 byte 数值(101, 104),但 `&str:nth`/`&str:first` 正确返回 1-char 字符串堆指针。
|
|
26
|
+
|
|
27
|
+
**修复**(`scripts/test-wasm.mjs`):添加 `readStr` 和 `checkStr` 辅助函数,改用字符串内容比较替代数值比较。
|
|
28
|
+
|
|
29
|
+
### 5. Clippy 告警修复
|
|
30
|
+
|
|
31
|
+
**修复**(`src/codegen/emit_wasm/runtime.rs`):为 `build_rt_hash_f64` 添加 `#[allow(dead_code)]`;为 `build_rt_generic_compare`、`build_rt_generic_eq`、`build_rt_hash_f64_semantic` 添加 `#[allow(clippy::vec_init_then_push)]`。
|
|
32
|
+
|
|
33
|
+
## 知识点
|
|
34
|
+
|
|
35
|
+
- WASM nil=0.0 的约束:所有核心函数如果对入参用 `number?` 进行断言,当传入 0 时会 trap。应直接 emit 低级指令绕过。
|
|
36
|
+
- 字符串比较应使用 `__rt_generic_eq` 而非 `F64Eq`(指针比较)。
|
|
37
|
+
- `&str:nth`/`&str:first` 返回 1-char 字符串(堆指针),不是 char code;test 脚本需用 `readStr` 读取内容比较。
|
|
38
|
+
- `&map:diff-new a b` 语义:返回 b 中不在 a 里的条目;参数顺序为 (a, b)。
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
- Fixed WASM `&str:nth` to return a one-character string or nil, matching native Calcit semantics.
|
|
2
|
+
- Added string-content-aware `=` handling in WASM while keeping `identical?` as raw-value equality.
|
|
3
|
+
- Inlined imported top-level value defs in WASM so string constants like `dictionary` are usable at runtime.
|
|
4
|
+
- Fixed `nil?` lowering in WASM to match the backend's nil representation.
|
|
5
|
+
- Corrected `&map:diff-new` argument semantics in WASM codegen.
|
|
6
|
+
- Updated WASM tests/docs to match current `&str:nth` semantics.
|
|
7
|
+
- Validated with `cargo clippy -- -D warnings`, `yarn compile`, `cargo test`, and `yarn check-all` before bumping version to `0.12.26`.
|
package/lib/package.json
CHANGED