@calcit/procs 0.12.19 → 0.12.20
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/202507161633-wasm-data-structures.md +61 -0
- package/editing-history/20260416-1936-predicate-narrowing-expansion.md +31 -0
- package/editing-history/202604161507-wasm-data-structures-and-rfc-rename.md +27 -0
- package/editing-history/202604161520-wasm-bitwise-and-match.md +21 -0
- package/editing-history/202604161542-wasm-cross-ns-host-imports.md +38 -0
- package/editing-history/20260417-0026-wasm-rest-args.md +62 -0
- package/editing-history/202604170048-wasm-type-of.md +70 -0
- package/editing-history/202604170051-wasm-derived-predicates.md +34 -0
- package/editing-history/202604170132-monomorphize-map-filter.md +50 -0
- package/editing-history/202604170135-monomorphize-includes-reverse.md +31 -0
- package/editing-history/202604170140-fold-type-predicates.md +44 -0
- package/editing-history/202604170154-generic-dispatch-records-tuples.md +52 -0
- package/lib/calcit.procs.mjs +39 -6
- package/lib/package.json +1 -1
- package/package.json +1 -1
- package/rfc/02-04-runtime-traits-plan.md +613 -0
- package/rfc/02-14-project-modernization-roadmap.md +229 -0
- package/rfc/02-17-register-platform-api-rfc.md +115 -0
- package/rfc/02-18-language-theory-evolution-plan.md +367 -0
- package/rfc/02-23-optional-record-macro-plan.md +30 -0
- package/rfc/03-05-function-schema-dual-track-rfc.md +162 -0
- package/rfc/03-16-runtime-boundary-refactor-plan.md +546 -0
- package/rfc/03-18-query-def-tree-show-chunked-display-plan.md +301 -0
- package/rfc/04-13-call-arg-literal-rewrite-rfc.md +205 -0
- package/rfc/04-13-type-slot-mechanism-rfc.md +194 -0
- package/rfc/04-15-match-syntax-rfc.md +175 -0
- package/rfc/04-15-type-directed-optimization-catalog.md +170 -0
- package/rfc/04-15-wasm-compilation-feasibility.md +236 -0
- package/rfc/04-16-wasm-data-structures.md +192 -0
- package/rfc/README.md +40 -0
- package/ts-src/calcit.procs.mts +33 -6
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# WASM Codegen — 数据结构支持
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
现有 WASM codegen(`emit_wasm.rs`)采用 all-f64 策略,仅支持纯数值函数。本文档描述如何在保持 all-f64 ABI 兼容的前提下,增量引入 Tag、Record、Tuple/Enum 等结构化数据的支持。
|
|
6
|
+
|
|
7
|
+
## 设计决策(继承自初始实现)
|
|
8
|
+
|
|
9
|
+
1. **二进制 `.wasm` 输出** — 使用 `wasm-encoder` crate 直接生成标准 WASM 二进制格式。
|
|
10
|
+
2. **All-f64 类型策略** — 所有值统一为 f64,Bool 用 1.0/0.0 表示。
|
|
11
|
+
3. **比较运算的 f64 返回** — 使用 `select` 指令将 i32 条件转为 f64 (1.0/0.0)。
|
|
12
|
+
4. **recur 映射到 WASM loop** — 通过 `block_depth` 追踪嵌套深度。
|
|
13
|
+
|
|
14
|
+
## 核心思路:线性内存 + f64 编码指针
|
|
15
|
+
|
|
16
|
+
### 值表示
|
|
17
|
+
|
|
18
|
+
保持所有函数参数和返回值为 f64,扩展值的语义:
|
|
19
|
+
|
|
20
|
+
| 值类型 | f64 表示方式 | 说明 |
|
|
21
|
+
|--------|-------------|------|
|
|
22
|
+
| Number | 直接 f64 | 不变 |
|
|
23
|
+
| Bool | 1.0 / 0.0 | 不变 |
|
|
24
|
+
| Nil | 0.0 | 不变 |
|
|
25
|
+
| Tag | 正整数 f64 (1.0, 2.0, ...) | 编译时分配,全局唯一 |
|
|
26
|
+
| Record 指针 | f64 编码的 i32 偏移 | 指向线性内存中的 Record 数据 |
|
|
27
|
+
| Tuple 指针 | f64 编码的 i32 偏移 | 指向线性内存中的 Tuple 数据 |
|
|
28
|
+
|
|
29
|
+
指针与数值之间的转换:
|
|
30
|
+
- 写入: `i32` → `f64.convert_i32_u`
|
|
31
|
+
- 读取: `f64` → `i32.trunc_f64_u`
|
|
32
|
+
|
|
33
|
+
### 歧义规避
|
|
34
|
+
|
|
35
|
+
Tag、指针、数值共用 f64 空间,依赖**编译时类型信息**区分:
|
|
36
|
+
- WASM 子集要求 Record/Tuple 相关函数必须有类型标注
|
|
37
|
+
- Tag 值与数值范围不重叠(Tag 从高位整数开始分配,或使用特殊编码)
|
|
38
|
+
- 实验阶段不做运行时类型检查,类型错误由 Calcit 预处理保证
|
|
39
|
+
|
|
40
|
+
## Tag 编译
|
|
41
|
+
|
|
42
|
+
Tag 在 Calcit 中是轻量标识符(如 `:ok`、`:err`、`:name`)。
|
|
43
|
+
|
|
44
|
+
### 方案
|
|
45
|
+
|
|
46
|
+
- 编译阶段遍历所有被使用的 Tag,分配从 `1.0` 开始的递增整数
|
|
47
|
+
- `Calcit::Tag(t)` 在 `emit_expr` 中编译为 `f64.const <tag_id>`
|
|
48
|
+
- Tag 比较复用现有 `f64.eq` 逻辑(`&=` 已支持)
|
|
49
|
+
|
|
50
|
+
### 编译上下文扩展
|
|
51
|
+
|
|
52
|
+
```rust
|
|
53
|
+
struct WasmGenCtx {
|
|
54
|
+
// ... existing fields ...
|
|
55
|
+
tag_index: HashMap<String, u32>, // tag name → integer ID
|
|
56
|
+
next_tag_id: u32, // counter, starts at 1
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Record 编译
|
|
61
|
+
|
|
62
|
+
### 内存布局
|
|
63
|
+
|
|
64
|
+
Record 在线性内存中按固定布局存储:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
offset + 0: struct_tag (f64, 8 bytes) — 标识 Record 类型
|
|
68
|
+
offset + 8: field_0 (f64, 8 bytes)
|
|
69
|
+
offset + 16: field_1 (f64, 8 bytes)
|
|
70
|
+
...
|
|
71
|
+
offset + 8*(n): field_{n-1} (f64, 8 bytes)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- 字段按 CalcitStruct.fields 的字母序排列(与 Calcit 语义一致)
|
|
75
|
+
- 每个 Record 占用 `8 * (1 + field_count)` 字节
|
|
76
|
+
|
|
77
|
+
### 操作映射
|
|
78
|
+
|
|
79
|
+
| Calcit 操作 | WASM 实现 |
|
|
80
|
+
|-------------|----------|
|
|
81
|
+
| `&%{} struct field1 val1 ...` | bump alloc + f64.store 每个字段 |
|
|
82
|
+
| `&record:get record :field` | `f64.load (ptr + field_offset)` |
|
|
83
|
+
| `&record:nth record idx tag` | `f64.load (ptr + (1 + idx) * 8)` |
|
|
84
|
+
| `&record:assoc record :field val` | 复制整个 Record + 修改指定字段 |
|
|
85
|
+
| `:field record` (tag-as-fn) | 等同于 `&record:get` |
|
|
86
|
+
|
|
87
|
+
### 内存分配
|
|
88
|
+
|
|
89
|
+
使用 bump allocator(单向增长,不回收):
|
|
90
|
+
|
|
91
|
+
```wasm
|
|
92
|
+
(global $heap_ptr (mut i32) (i32.const 0))
|
|
93
|
+
|
|
94
|
+
;; alloc(size: i32) -> i32
|
|
95
|
+
(func $alloc (param $size i32) (result i32)
|
|
96
|
+
(local $ptr i32)
|
|
97
|
+
(local.set $ptr (global.get $heap_ptr))
|
|
98
|
+
(global.set $heap_ptr (i32.add (global.get $heap_ptr) (local.get $size)))
|
|
99
|
+
(local.get $ptr)
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- 初始 heap_ptr = 0(或留出少许保留空间)
|
|
104
|
+
- 线性内存初始 1 page (64KB),不够时 `memory.grow`
|
|
105
|
+
- 实验阶段不实现 GC,适用于短生命周期的计算任务
|
|
106
|
+
|
|
107
|
+
## Tuple/Enum 编译
|
|
108
|
+
|
|
109
|
+
### 内存布局
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
offset + 0: variant_tag (f64, 8 bytes) — 对应 EnumVariant 的 tag ID
|
|
113
|
+
offset + 8: payload_0 (f64, 8 bytes)
|
|
114
|
+
offset + 16: payload_1 (f64, 8 bytes)
|
|
115
|
+
...
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 操作映射
|
|
119
|
+
|
|
120
|
+
| Calcit 操作 | WASM 实现 |
|
|
121
|
+
|-------------|----------|
|
|
122
|
+
| `:: tag val1 val2 ...` | bump alloc + store tag + payloads |
|
|
123
|
+
| `&tuple:nth tuple idx` | `f64.load (ptr + (1 + idx) * 8)` |
|
|
124
|
+
| `tag-match` | load tag → if/else chain(小量 variants)或 `br_table`(多 variants) |
|
|
125
|
+
|
|
126
|
+
### tag-match 编译
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
;; tag-match expr
|
|
130
|
+
;; (:ok val) -> body1
|
|
131
|
+
;; (:err msg) -> body2
|
|
132
|
+
f64.load ptr ;; load variant tag
|
|
133
|
+
i32.trunc_f64_u
|
|
134
|
+
br_table 0 1 2 ;; jump to branch (default to last)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
对于少量分支(≤4),直接用 if/else 链;多分支用 `br_table`。
|
|
138
|
+
|
|
139
|
+
## List 支持(暂缓)
|
|
140
|
+
|
|
141
|
+
List 在 Calcit 中是持久化数据结构(`Vector(Vec)` 或 `TernaryTreeList`),WASM 中实现完整语义工作量大。
|
|
142
|
+
|
|
143
|
+
### 初步方案(仅 stub)
|
|
144
|
+
|
|
145
|
+
- `&list:nth` / `count` 可对定长数组实现
|
|
146
|
+
- `foldl` / `map` 等高阶操作暂不支持(需要闭包/函数指针)
|
|
147
|
+
- 本轮仅添加 `Err("List not yet supported in WASM codegen")`
|
|
148
|
+
|
|
149
|
+
## String 支持(评估阶段)
|
|
150
|
+
|
|
151
|
+
### 方案对比
|
|
152
|
+
|
|
153
|
+
| 方案 | 复杂度 | 适用性 |
|
|
154
|
+
|------|--------|--------|
|
|
155
|
+
| Host import (JS 侧处理) | 低 | 适合 JS 互操作场景 |
|
|
156
|
+
| 线性内存 UTF-8 | 中 | 需要自己维护 string pool |
|
|
157
|
+
| WASM GC string (proposal) | 低 | 需要运行时支持 (V8/Deno ✅) |
|
|
158
|
+
|
|
159
|
+
### 结论
|
|
160
|
+
|
|
161
|
+
本轮不实现 String。推荐路径:
|
|
162
|
+
1. **近期**: host import 方案(`println` 等通过 host function 代理)
|
|
163
|
+
2. **远期**: WASM GC string 标准化后直接采用
|
|
164
|
+
|
|
165
|
+
## 改进路线
|
|
166
|
+
|
|
167
|
+
### 近期(低投入)
|
|
168
|
+
|
|
169
|
+
- 输出路径配置(`--emit-path`)
|
|
170
|
+
- 跨命名空间函数调用
|
|
171
|
+
- `pow` / `sin` / `cos` 通过 host import
|
|
172
|
+
- let 嵌套合并优化
|
|
173
|
+
|
|
174
|
+
### 中期(本文档主题)
|
|
175
|
+
|
|
176
|
+
- ✅ Tag 编译为整数常量
|
|
177
|
+
- Record 线性内存布局 + 基本操作
|
|
178
|
+
- Tuple/Enum 线性内存布局 + tag-match
|
|
179
|
+
- JS 互操作桥接
|
|
180
|
+
|
|
181
|
+
### 远期
|
|
182
|
+
|
|
183
|
+
- List 完整支持(可能需要 WASM GC)
|
|
184
|
+
- String 支持
|
|
185
|
+
- 完整类型推导
|
|
186
|
+
- 多模块链接
|
|
187
|
+
|
|
188
|
+
## 参考
|
|
189
|
+
|
|
190
|
+
- 可行性评估: `rfc/04-15-wasm-compilation-feasibility.md`
|
|
191
|
+
- 优化目录: `rfc/04-15-type-directed-optimization-catalog.md`
|
|
192
|
+
- 用法文档: `docs/wasm-codegen.md`
|
package/rfc/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# RFC 整理索引
|
|
2
|
+
|
|
3
|
+
更新时间:2026-04-17
|
|
4
|
+
|
|
5
|
+
## 目录原则
|
|
6
|
+
|
|
7
|
+
- 这里只保留**仍适合未来继续阅读**的草案;
|
|
8
|
+
- 已完成、已过时、会误导后续实现的内容,直接删除;
|
|
9
|
+
- 历史过程统一以 `editing-history/` 为准,不再在 rfc 中重复保留。
|
|
10
|
+
|
|
11
|
+
## 当前保留文件
|
|
12
|
+
|
|
13
|
+
| 文件 | 状态 | 建议 |
|
|
14
|
+
| ----------------------------------- | ------------- | ---------------------------------------------------------- |
|
|
15
|
+
| `03-05-function-schema-dual-track-rfc.md` | Active | 已收敛为当前 schema 约定说明,涉及函数 schema 时优先参考。 |
|
|
16
|
+
| `02-04-runtime-traits-plan.md` | Active | runtime traits 主设计文档。 |
|
|
17
|
+
| `02-17-register-platform-api-rfc.md` | Active | host capability / register API 规范草案。 |
|
|
18
|
+
| `02-18-language-theory-evolution-plan.md` | Review-needed | 偏理论路线图,阅读时需区分愿景与已落地内容。 |
|
|
19
|
+
| `02-23-optional-record-macro-plan.md` | Review-needed | 小范围提案,尚未进入稳定实现。 |
|
|
20
|
+
| `02-14-project-modernization-roadmap.md` | Review-needed | 工程路线图可参考,但不要当作语法或行为文档。 |
|
|
21
|
+
| `03-16-runtime-boundary-refactor-plan.md` | Review-needed | 运行时边界重构方案。 |
|
|
22
|
+
| `03-18-query-def-tree-show-chunked-display-plan.md` | Review-needed | query/tree show 分块展示方案。 |
|
|
23
|
+
| `04-13-call-arg-literal-rewrite-rfc.md` | Active | 调用参数字面量重写优化提案。 |
|
|
24
|
+
| `04-13-type-slot-mechanism-rfc.md` | Active | Type slot 机制提案。 |
|
|
25
|
+
| `04-15-match-syntax-rfc.md` | Active | match 语法改进提案。 |
|
|
26
|
+
| `04-15-type-directed-optimization-catalog.md` | Active | 基于 `&record:nth` 经验,系统梳理 Record/Tuple/Scope 等类型导向优化机会。 |
|
|
27
|
+
| `04-15-wasm-compilation-feasibility.md` | Active | WASM 编译三条路径(解释器→WASM / AOT 子集 / WASM GC)的可行性评估。 |
|
|
28
|
+
| `04-16-wasm-data-structures.md` | Active | WASM codegen 中 Tag/Record/Tuple 等数据结构的内存布局与编译策略。 |
|
|
29
|
+
|
|
30
|
+
## 已执行的清理
|
|
31
|
+
|
|
32
|
+
- 删除了旧的类型标注/泛型函数草案,避免继续传播过时 `hint-fn`、旧函数类型 DSL 与迁移期描述;
|
|
33
|
+
- 删除了旧审查报告和 archived 下的历史快照,避免大模型后续检索到过时语义;
|
|
34
|
+
- 空的 `archived/` 目录不再作为阅读入口使用。
|
|
35
|
+
|
|
36
|
+
## 后续维护规则
|
|
37
|
+
|
|
38
|
+
1. 若文档里的语法示例已经不符合当前实现,优先修正;
|
|
39
|
+
2. 若文档主要价值只剩“历史过程”,优先删除并让位给 `editing-history/`;
|
|
40
|
+
3. 若文档保留,至少应保证示例语法与当前代码库一致。
|
package/ts-src/calcit.procs.mts
CHANGED
|
@@ -146,11 +146,20 @@ export let _$n_assert_traits = function (value: CalcitValue, traitDef: CalcitVal
|
|
|
146
146
|
if (!(traitDef instanceof CalcitTrait)) {
|
|
147
147
|
throw new Error(`&assert-traits expected a trait definition, but received: ${toString(traitDef, true)}`);
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
// For records/tuples, only check instance impls (not builtin fallbacks),
|
|
150
|
+
// matching the Rust runtime behavior of collect_impl_records_for_value.
|
|
151
|
+
let impls: CalcitImpl[];
|
|
152
|
+
if (value instanceof CalcitRecord) {
|
|
153
|
+
impls = value.structRef.impls ?? [];
|
|
154
|
+
} else if (value instanceof CalcitTuple) {
|
|
155
|
+
impls = value.impls ?? [];
|
|
156
|
+
} else {
|
|
157
|
+
const pair = lookup_impls(value);
|
|
158
|
+
if (pair == null) {
|
|
159
|
+
throw new Error(`&assert-traits cannot resolve impls for: ${toString(value, true)}`);
|
|
160
|
+
}
|
|
161
|
+
impls = pair[0];
|
|
152
162
|
}
|
|
153
|
-
const impls = pair[0];
|
|
154
163
|
const missing: string[] = [];
|
|
155
164
|
for (let i = 0; i < traitDef.methods.length; i++) {
|
|
156
165
|
const method = traitDef.methods[i];
|
|
@@ -1792,6 +1801,8 @@ let calcit_builtin_impls = {
|
|
|
1792
1801
|
list: null as CalcitImplEntry,
|
|
1793
1802
|
map: null as CalcitImplEntry,
|
|
1794
1803
|
fn: null as CalcitImplEntry,
|
|
1804
|
+
tuple: null as CalcitImplEntry,
|
|
1805
|
+
record: null as CalcitImplEntry,
|
|
1795
1806
|
};
|
|
1796
1807
|
|
|
1797
1808
|
// need to register code from outside
|
|
@@ -1831,10 +1842,26 @@ function lookup_impls(obj: CalcitValue): [CalcitImpl[], string] {
|
|
|
1831
1842
|
impls = normalize_builtin_impls(calcit_builtin_impls.map);
|
|
1832
1843
|
} else if (obj instanceof CalcitRecord) {
|
|
1833
1844
|
tag = obj.name.toString();
|
|
1834
|
-
|
|
1845
|
+
let instanceImpls = obj.structRef.impls;
|
|
1846
|
+
let builtinRecordImpls = normalize_builtin_impls(calcit_builtin_impls.record);
|
|
1847
|
+
if (builtinRecordImpls && instanceImpls && instanceImpls.length > 0) {
|
|
1848
|
+
impls = [...builtinRecordImpls, ...instanceImpls];
|
|
1849
|
+
} else if (builtinRecordImpls) {
|
|
1850
|
+
impls = builtinRecordImpls;
|
|
1851
|
+
} else {
|
|
1852
|
+
impls = instanceImpls;
|
|
1853
|
+
}
|
|
1835
1854
|
} else if (obj instanceof CalcitTuple) {
|
|
1836
1855
|
tag = obj.tag.toString();
|
|
1837
|
-
|
|
1856
|
+
let instanceImpls = obj.impls;
|
|
1857
|
+
let builtinTupleImpls = normalize_builtin_impls(calcit_builtin_impls.tuple);
|
|
1858
|
+
if (builtinTupleImpls && instanceImpls && instanceImpls.length > 0) {
|
|
1859
|
+
impls = [...builtinTupleImpls, ...instanceImpls];
|
|
1860
|
+
} else if (builtinTupleImpls) {
|
|
1861
|
+
impls = builtinTupleImpls;
|
|
1862
|
+
} else {
|
|
1863
|
+
impls = instanceImpls;
|
|
1864
|
+
}
|
|
1838
1865
|
} else if (obj instanceof CalcitSet) {
|
|
1839
1866
|
tag = "&core-set-methods";
|
|
1840
1867
|
impls = normalize_builtin_impls(calcit_builtin_impls.set);
|