@calcit/procs 0.12.17 → 0.12.19
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/202504160117-wasm-codegen-and-catalog-update.md +40 -0
- package/editing-history/202507160207-wasm-encoder-migration.md +48 -0
- package/editing-history/{202604132309-typeslot-enum-compile-safety.md → 2026-0413-2309-typeslot-enum-compile-safety.md} +5 -0
- package/editing-history/2026-0414-1500-loose-record-syntax.md +32 -0
- package/editing-history/2026-0414-2259-bidir-type-check-refactor.md +63 -0
- package/editing-history/2026-0415-0120-match-syntax-exhaustiveness.md +20 -0
- package/editing-history/202604151211-record-nth-optimization.md +44 -0
- package/editing-history/202604160131-wasm-math-and-test-integration.md +28 -0
- package/lib/js-record.mjs +23 -1
- package/lib/package.json +5 -4
- package/package.json +5 -4
- package/ts-src/js-record.mts +23 -0
- /package/editing-history/{202504121453-map-to-record-rewrite.md → 2025-0412-1453-map-to-record-rewrite.md} +0 -0
- /package/editing-history/{202604121524-js-codegen-map-to-record.md → 2026-0412-1524-js-codegen-map-to-record.md} +0 -0
- /package/editing-history/{202604121549-map-to-record-field-validation.md → 2026-0412-1549-map-to-record-field-validation.md} +0 -0
- /package/editing-history/{202604131600-tuple-to-enum-rewrite.md → 2026-0413-1600-tuple-to-enum-rewrite.md} +0 -0
package/.yarn/install-state.gz
CHANGED
|
Binary file
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# 202504160117 — 优化目录更新 + WASM codegen demo
|
|
2
|
+
|
|
3
|
+
## 优化目录更新
|
|
4
|
+
|
|
5
|
+
更新 `drafts/04-15-type-directed-optimization-catalog.md`:
|
|
6
|
+
|
|
7
|
+
- 标记 P0、P1、P2、P5、P7 为已完成,附 commit hash
|
|
8
|
+
- 标记 P3、P4 为推迟,附推迟原因
|
|
9
|
+
- 更新 Scope 行:从 `TernaryTreeList<ScopePair>` 改为 `Vec<ScopePair>`
|
|
10
|
+
- P5 描述更新为实际采用的 Vec 方案(而非原规划的 O(1) 方案)
|
|
11
|
+
- 新增「执行记录」节替代原「推荐执行顺序」,含基准数据(836ms→718ms,~14%)
|
|
12
|
+
|
|
13
|
+
## WASM codegen 实验性功能
|
|
14
|
+
|
|
15
|
+
### 新增文件
|
|
16
|
+
|
|
17
|
+
- `src/codegen/emit_wasm.rs` — ~440 行 WAT 代码生成器
|
|
18
|
+
- `demos/wasm-demo.cirru` — 示例程序(fibo、factorial、add-two)
|
|
19
|
+
- `docs/wasm-codegen.md` — 使用文档 + 未来改进路线
|
|
20
|
+
|
|
21
|
+
### 修改文件
|
|
22
|
+
|
|
23
|
+
- `src/codegen.rs` — 注册 `emit_wasm` 模块
|
|
24
|
+
- `src/cli_args.rs` — 新增 `EmitWasmCommand`(`cr <entry> wasm`)
|
|
25
|
+
- `src/bin/cr.rs` — 新增 `run_wasm_codegen` 入口
|
|
26
|
+
|
|
27
|
+
### 设计要点
|
|
28
|
+
|
|
29
|
+
- **纯 WAT 文本输出** — 零额外依赖,不引入 wasm-encoder 等 crate
|
|
30
|
+
- **All-f64 类型策略** — 所有值统一为 f64,Bool 用 1.0/0.0
|
|
31
|
+
- **支持子集** — defn、if、let、算术(&+/&-/&\*/&/)、比较(&</&>/&=)、recur、函数调用
|
|
32
|
+
- **不支持** — 字符串、Record/Map/Set、method dispatch、IO、可变参数
|
|
33
|
+
|
|
34
|
+
### 验证
|
|
35
|
+
|
|
36
|
+
- wasmtime compile 验证 WAT 合法
|
|
37
|
+
- wasmtime run 验证 fibo(10)=89、factorial(10)=3628800、add-two(3.5,2.5)=6
|
|
38
|
+
- cargo test 246/246 pass
|
|
39
|
+
- cargo clippy -- -D warnings clean
|
|
40
|
+
- yarn check-all pass
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# wasm-encoder 迁移:WAT 文本 → 二进制 .wasm
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
将 WASM codegen 从手工拼接 WAT 文本字符串改为使用 `wasm-encoder` crate 直接生成二进制 `.wasm`,
|
|
6
|
+
同时将测试运行时从 wasmtime CLI 替换为 Node.js 内置的 `WebAssembly` API。
|
|
7
|
+
|
|
8
|
+
## 动机
|
|
9
|
+
|
|
10
|
+
- wasmtime 是重量级依赖,CI 需要额外安装步骤,且跨版本 CLI 接口不稳定(v14/v25 breaking changes)
|
|
11
|
+
- WAT 字符串拼接容易出错且难以调试
|
|
12
|
+
- Node.js 自带 WebAssembly 运行时,零额外依赖
|
|
13
|
+
|
|
14
|
+
## 关键知识点
|
|
15
|
+
|
|
16
|
+
### wasm-encoder 用法
|
|
17
|
+
|
|
18
|
+
- `wasm-encoder = "0.246.2"`,仅 1 个传递依赖 `leb128fmt`
|
|
19
|
+
- TypeSection → FunctionSection → ExportSection → CodeSection → module.finish()
|
|
20
|
+
- `F64Const` 接受 `Ieee64` 而非 `f64`,需 `Ieee64::from(val)` 包装
|
|
21
|
+
- `Function::new(locals_vec)` + `instruction(&Instruction::...)` + `instruction(&Instruction::End)`
|
|
22
|
+
|
|
23
|
+
### WASM block depth 追踪
|
|
24
|
+
|
|
25
|
+
- `br N` 中 N 是相对深度(0 = 当前块)
|
|
26
|
+
- `if` 块会增加一层嵌套,所以在 `if` 内部 `br` 到外层 `loop` 需要 depth=1
|
|
27
|
+
- 解决方案:`WasmGenCtx` 添加 `block_depth` 字段,`emit_if` 时 +1,`recur` 使用 `Br(ctx.block_depth)`
|
|
28
|
+
|
|
29
|
+
### 两阶段编译
|
|
30
|
+
|
|
31
|
+
- 函数互调需要预知 fn_index,所以:
|
|
32
|
+
1. 第一遍:收集函数签名和索引映射 `HashMap<String, u32>`
|
|
33
|
+
2. 第二遍:编译函数体,失败的函数用 stub `[f64_const(0.0)]` 保持索引稳定
|
|
34
|
+
|
|
35
|
+
### 比较运算的 i32→f64 转换
|
|
36
|
+
|
|
37
|
+
- WASM 比较指令(f64.lt/gt/eq)返回 i32
|
|
38
|
+
- 用 `select(1.0, 0.0, cmp_result)` 转为 f64
|
|
39
|
+
- `if` 条件用 `f64.ne(val, 0.0)` 转回 i32
|
|
40
|
+
|
|
41
|
+
## 修改文件
|
|
42
|
+
|
|
43
|
+
- `src/codegen/emit_wasm.rs` — 完全重写
|
|
44
|
+
- `scripts/test-wasm.mjs` — 新增 Node.js 测试运行器
|
|
45
|
+
- `scripts/test-wasm.sh` — 简化为生成 + node 调用
|
|
46
|
+
- `.github/workflows/test.yaml` — 移除 wasmtime 安装步骤
|
|
47
|
+
- `Cargo.toml` / `Cargo.lock` — 添加 wasm-encoder
|
|
48
|
+
- `docs/wasm-codegen.md` — 更新文档
|
|
@@ -7,22 +7,27 @@
|
|
|
7
7
|
## 修改的文件
|
|
8
8
|
|
|
9
9
|
### `src/calcit/type_annotation.rs`
|
|
10
|
+
|
|
10
11
|
- `resolve_to_enum_with_ref()`: 新增 `TypeSlot` 分支,通过 `resolve_type_slot()` 解析绑定类型后委托
|
|
11
12
|
- `matches_with_bindings()`: 双向 `Tuple/Enum` 匹配 — 原先只有 `(Tuple, Enum)` 方向,TypeSlot 解析后顺序会翻转,新增 `(Enum, Tuple)` 方向
|
|
12
13
|
|
|
13
14
|
### `src/calcit.rs`
|
|
15
|
+
|
|
14
16
|
- 将 `resolve_type_slot` 加入 `pub use` 导出列表
|
|
15
17
|
|
|
16
18
|
### `src/runner/preprocess.rs`
|
|
19
|
+
|
|
17
20
|
- `resolve_enum_value()`: 在 `as_struct()` 调用前增加 TypeSlot 解析(避免 `&CalcitStruct` 生命周期问题)
|
|
18
21
|
- 新增 `try_rewrite_local_fn_tuple_args_to_enum_tuples()`: 对本地函数调用(如回调 `d!`)也执行 tuple→enum 自动重写
|
|
19
22
|
- 主预处理 `Calcit::Local` 分支: 增加 local fn 调用的重写+类型检查(clone 避免借用冲突)
|
|
20
23
|
- `try_rewrite_single_tuple_to_enum_tuple()`: 增加变体验证 — 重写后立即检查 tag 是否是 enum 的合法 variant,不合法则发出警告
|
|
21
24
|
|
|
22
25
|
### `src/bin/cr_tests/type_fail.rs`
|
|
26
|
+
|
|
23
27
|
- 新增 `type_fail_type_slot_enum_invalid_variant` 测试,验证 TypeSlot 绑定的 enum 在自动重写路径下能正确检测不存在的 variant
|
|
24
28
|
|
|
25
29
|
### `calcit/type-fail/type-slot-enum-invalid-variant.cirru`
|
|
30
|
+
|
|
26
31
|
- 新增测试 fixture:声明 `defenum Action`、通过 TypeSlot 绑定、调用 `takes-action $ :: :nonexistent |hello`,期望产生 variant 不存在警告
|
|
27
32
|
|
|
28
33
|
## 知识点
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 2026-04-14 15:00 — `?{}` 松散 Record 语法
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
新增 `?{}` 松散 record 语法,允许不声明 struct 就创建 record,与 `::` 无类型 tuple 对称。
|
|
6
|
+
在函数参数有 struct 类型标注时,预处理阶段自动改写为 `%{} StructDef ...`。
|
|
7
|
+
|
|
8
|
+
## 改动文件
|
|
9
|
+
|
|
10
|
+
| 文件 | 变更 |
|
|
11
|
+
|------|------|
|
|
12
|
+
| `src/calcit/proc_name.rs` | 新增 `NativeLooseRecord` proc 变体,`?{}` 序列化,类型签名 |
|
|
13
|
+
| `src/data/cirru.rs` | `"?{}"` token 解析为 `Calcit::Proc(NativeLooseRecord)` |
|
|
14
|
+
| `src/calcit/record.rs` | `LOOSE_RECORD_NAME`、`is_loose()`、`from_loose_pairs()` |
|
|
15
|
+
| `src/builtins/records.rs` | `call_loose_record()` 运行时:验证 tag 键、排序、排重 |
|
|
16
|
+
| `src/builtins.rs` | 分发 `NativeLooseRecord => call_loose_record(args)` |
|
|
17
|
+
| `src/calcit.rs` | Display: 松散 record 显示为 `(?{} ...)` |
|
|
18
|
+
| `src/runner/preprocess.rs` | `try_rewrite_loose_record_args_to_struct_records`、类型推断、skip core arg check |
|
|
19
|
+
| `ts-src/js-record.mts` | JS 运行时 `_$q__$M_` 函数 |
|
|
20
|
+
| `calcit/test-record.cirru` | `test-loose-record-rewrite` 集成测试 |
|
|
21
|
+
| `docs/features/records.md` | 文档:松散 record 语法、自动改写、类型对称表 |
|
|
22
|
+
|
|
23
|
+
## 性能优化
|
|
24
|
+
|
|
25
|
+
- **预处理改写链合并重建**:三步改写(map→record、loose→struct、tuple→enum)原先每步都重建 `ys` (CalcitList),优化为仅在有改写时统一重建一次,减少 CalcitList push 操作。
|
|
26
|
+
- **JS 排序比较器修正**:将重复字段检测从 sort comparator 中移出(comparator 内 throw 违反排序契约),改为排序后线性扫描相邻元素,行为更可靠。
|
|
27
|
+
|
|
28
|
+
## 知识点
|
|
29
|
+
|
|
30
|
+
- `EdnTag::cmp` 按全局 tag 池分配顺序排序(整数 index),**不是**字典序。record 字段排序必须用 `ref_str().cmp()` 保持字母序。
|
|
31
|
+
- CalcitList 基于 persistent ternary tree,`.clone()` 是 O(1) 引用计数操作。
|
|
32
|
+
- Sort comparator 不应抛异常——不同排序算法对异常行为未定义。
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 202604142259 — Bidirectional Type Checking Architecture Refactoring
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
将 `src/runner/preprocess.rs`(6199 行单体文件)拆分为模块化目录结构,
|
|
6
|
+
按双向类型检查(bidirectional type checking)的概念分离关注点。
|
|
7
|
+
|
|
8
|
+
## 改动详情
|
|
9
|
+
|
|
10
|
+
### 模块拆分
|
|
11
|
+
|
|
12
|
+
`preprocess.rs` → `preprocess/` 目录模块:
|
|
13
|
+
|
|
14
|
+
| 模块 | 行数 | 职责 |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `mod.rs` | ~4744 | 预处理核心:符号解析、宏展开、作用域管理、特殊语法 |
|
|
17
|
+
| `type_inference.rs` | ~693 | 类型合成(synthesis):自下而上推导表达式类型 |
|
|
18
|
+
| `type_checking.rs` | ~402 | 类型检查(checking):自上而下验证参数类型 |
|
|
19
|
+
| `type_rewriting.rs` | ~398 | 类型定向重写:根据期望类型重写 AST 字面量 |
|
|
20
|
+
|
|
21
|
+
### 代码去重
|
|
22
|
+
|
|
23
|
+
1. **统一参数重写循环** (`rewrite_args_by_expected_type`):
|
|
24
|
+
- 原先 4 个 `try_rewrite_*_args_to_*` 函数各自包含相同的外层循环(~30 LOC × 4)
|
|
25
|
+
- 提取为泛型 `rewrite_args_by_expected_type<F>` 函数,4 个入口各只保留闭包调用
|
|
26
|
+
|
|
27
|
+
2. **统一参数类型检查循环** (`check_arg_types_loop`):
|
|
28
|
+
- 原先 `check_local_fn_call_arg_types` 和 `check_user_fn_arg_types` 共享相同的
|
|
29
|
+
zip → resolve_type_value → matches_with_bindings → gen_check_warning 模式
|
|
30
|
+
- 提取为泛型 `check_arg_types_loop<F>` 函数,包含 variadic 处理和 spread 跳过逻辑
|
|
31
|
+
|
|
32
|
+
3. **统一引用节点构建** (`build_struct_ref_node` / `build_enum_ref_node`):
|
|
33
|
+
- 原先 map→record 和 loose-record→struct 各自复制 Import vs Struct/Enum 分支
|
|
34
|
+
- 提取为共享的 `build_struct_ref_node` 和 `build_enum_ref_node` 函数
|
|
35
|
+
|
|
36
|
+
### Proc 推导提取
|
|
37
|
+
|
|
38
|
+
- `infer_type_from_expr` 中的 `Calcit::Proc` 分支(~100 行 match arms)
|
|
39
|
+
提取为独立的 `infer_proc_call_return_type` 函数,提高可读性
|
|
40
|
+
|
|
41
|
+
## 双向类型检查映射
|
|
42
|
+
|
|
43
|
+
参照 Dunfield & Krishnaswami 的框架:
|
|
44
|
+
|
|
45
|
+
| 方向 | 模块 | 入口函数 |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| **Synthesis** (⇒) | `type_inference` | `infer_type_from_expr`, `resolve_type_value` |
|
|
48
|
+
| **Checking** (⇐) | `type_checking` | `check_*_arg_types`, `check_function_return_type` |
|
|
49
|
+
| **Rewriting** (结构适配) | `type_rewriting` | `try_rewrite_*` |
|
|
50
|
+
| **Context** (线程局部) | `mod.rs` | `EXPECTED_FN_TYPE`, `EXPECTED_STRUCT_TYPE` |
|
|
51
|
+
|
|
52
|
+
## 为后续 match 语法扩展铺路
|
|
53
|
+
|
|
54
|
+
- `type_checking.rs` 的 `check_arg_types_loop` 模式可直接复用于 match 分支检查
|
|
55
|
+
- `type_inference.rs` 中已有 `infer_if_return_type` 作为分支合并的模板
|
|
56
|
+
- 未来只需在相应模块中添加新函数,无需修改 `mod.rs` 的核心调度
|
|
57
|
+
|
|
58
|
+
## 验证
|
|
59
|
+
|
|
60
|
+
- `cargo check --lib` ✅ 零警告
|
|
61
|
+
- `cargo clippy --lib -- -D warnings` ✅ 零警告
|
|
62
|
+
- `cargo fmt` ✅ 格式一致
|
|
63
|
+
- 链接器问题(`ld: library 'System' not found`)是环境问题,非代码变更引起
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# match syntax with exhaustiveness checking
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Added native `match` syntax for enum pattern matching with compile-time exhaustiveness detection,
|
|
6
|
+
as a safer alternative to `tag-match` macro. See `drafts/match-syntax-rfc.md` for full design.
|
|
7
|
+
|
|
8
|
+
## Files modified
|
|
9
|
+
|
|
10
|
+
- `src/calcit/syntax_name.rs` — `CalcitSyntax::Match`
|
|
11
|
+
- `src/builtins/syntax.rs` — `syntax_match()` runtime
|
|
12
|
+
- `src/builtins.rs` — dispatch entry
|
|
13
|
+
- `src/runner/preprocess/mod.rs` — `preprocess_match()` with exhaustiveness
|
|
14
|
+
- `src/codegen/emit_js.rs` — `gen_match_code()` JS output
|
|
15
|
+
- `calcit/test-enum.cirru` — test cases
|
|
16
|
+
|
|
17
|
+
## Key takeaway
|
|
18
|
+
|
|
19
|
+
Cirru indentation creates pair-based `(pattern body)` children — same format as `tag-match`.
|
|
20
|
+
Flat alternating format doesn't survive Cirru serialization round-trips.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# `&record:nth` — Compile-Time Record Field Index Optimization
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
新增 `NativeRecordNth` / `&record:nth` 过程,在预处理阶段将 record 字段访问优化为 O(1) 索引访问,绕过运行时的类型分发链和二分查找。
|
|
6
|
+
|
|
7
|
+
## 优化前访问路径
|
|
8
|
+
|
|
9
|
+
- `(:field record)` → 重写为 `(get arg :field)` → 运行时 6 次类型判断 (`nil?` → `string?` → `map?` → `list?` → `tuple?` → `record?`) → `&record:get` → `index_of()` O(log n) 二分查找
|
|
10
|
+
- `&record:get record :field` → 运行时 `index_of()` O(log n) 二分查找
|
|
11
|
+
|
|
12
|
+
## 优化后访问路径
|
|
13
|
+
|
|
14
|
+
当预处理阶段可以通过 `resolve_type_value` + `as_struct()` 解析出 record 的 struct 定义时:
|
|
15
|
+
|
|
16
|
+
- `(:field record)` → 直接重写为 `(&record:nth record <compile-time-idx>)` → 运行时 O(1) `values[idx]`
|
|
17
|
+
- `&record:get record :field` → 重写为 `(&record:nth record <compile-time-idx>)` → 运行时 O(1)
|
|
18
|
+
|
|
19
|
+
## 触发条件
|
|
20
|
+
|
|
21
|
+
优化仅在类型信息明确时激活:
|
|
22
|
+
- record 由 `%Struct ...` 构造器在本地 `let` 中创建
|
|
23
|
+
- 函数参数有显式 struct 类型的 schema 标注
|
|
24
|
+
|
|
25
|
+
若类型不明,回 fallback 到原有路径,不影响运行时行为。
|
|
26
|
+
|
|
27
|
+
## 修改文件
|
|
28
|
+
|
|
29
|
+
1. **`src/calcit/proc_name.rs`** — 新增 `NativeRecordNth` 变体 (`&record:nth`),签名 `(record, number) → dynamic`
|
|
30
|
+
2. **`src/builtins/records.rs`** — 实现 `record_nth(xs)` 运行时函数,直接 `values[idx]` 访问
|
|
31
|
+
3. **`src/builtins.rs`** — 分发表连线 `NativeRecordNth => records::record_nth`
|
|
32
|
+
4. **`src/runner/preprocess/mod.rs`** — 两个重写点:
|
|
33
|
+
- Tag-call (`Calcit::Tag` as head):解析 arg 类型后索引重写
|
|
34
|
+
- `NativeRecordGet` proc call:解析 record arg 类型后索引重写
|
|
35
|
+
5. **`src/runner/preprocess/type_inference.rs`** — `infer_record_nth_type()` 根据 struct 的 `field_types[idx]` 推断返回类型
|
|
36
|
+
6. **`src/runner/preprocess/type_checking.rs`** — 将 `NativeRecordNth` 加入 core arg type check 跳过列表
|
|
37
|
+
7. **`src/codegen/emit_js.rs`** — JS 内联代码生成 `record.values[idx]`,无函数调用开销
|
|
38
|
+
|
|
39
|
+
## 知识点
|
|
40
|
+
|
|
41
|
+
- `CalcitStruct.index_of(field_str)` 在编译期使用,用字段名解析排序位置
|
|
42
|
+
- `resolve_type_value()` 返回 `None` 时优化不触发,安全回退
|
|
43
|
+
- JS CalcitRecord 的 `.values` 数组和 Rust 端的 `values` Vec 索引一致(均按字段名字母序排序)
|
|
44
|
+
- 对于真实项目(如 respo),函数参数通常缺少 struct 类型标注,优化暂不触发;后续若 schema 推断增强,无需改动 rewrite 逻辑即可自动生效
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 202604160131 — WASM codegen 扩展: 数学函数 + 测试集成
|
|
2
|
+
|
|
3
|
+
## 新增 WASM 操作
|
|
4
|
+
|
|
5
|
+
- `floor` → `f64.floor`
|
|
6
|
+
- `ceil` → `f64.ceil`
|
|
7
|
+
- `round` → `f64.nearest`
|
|
8
|
+
- `sqrt` → `f64.sqrt`
|
|
9
|
+
- `identical?` → `f64.eq`
|
|
10
|
+
- `sin`/`cos`/`pow` 明确报错(WASM 无对应指令)
|
|
11
|
+
|
|
12
|
+
## 新增文件
|
|
13
|
+
|
|
14
|
+
- `calcit/test-wasm.cirru` — 17 个纯数值函数测试(fibo, factorial, add-two, sum-range, floor, ceil, round, sqrt, rem, compare, not, let-chain, collatz-steps, gcd 等)
|
|
15
|
+
- `scripts/test-wasm.sh` — WASM 验证脚本(生成 WAT → wasmtime compile → 逐函数验证返回值)
|
|
16
|
+
|
|
17
|
+
## 修改文件
|
|
18
|
+
|
|
19
|
+
- `src/codegen/emit_wasm.rs` — 新增 `unary_op` 辅助函数, 添加 Floor/Ceil/Round/Sqrt/Identical 分支
|
|
20
|
+
- `package.json` — `check-all` 加入 `try-wasm` 步骤; 新增 `try-wasm` 脚本
|
|
21
|
+
- `docs/wasm-codegen.md` — 更新支持列表、测试说明、路线图
|
|
22
|
+
|
|
23
|
+
## 验证
|
|
24
|
+
|
|
25
|
+
- `yarn check-all` 通过(含 compile, try-rs, try-js, try-ir, try-wasm)
|
|
26
|
+
- `cargo test` 246/246 pass
|
|
27
|
+
- `cargo clippy -- -D warnings` clean
|
|
28
|
+
- wasmtime 逐函数验证: 17/17 assertions pass
|
package/lib/js-record.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CalcitImpl } from "./js-impl.mjs";
|
|
2
|
-
import { castTag, toString, findInFields } from "./calcit-data.mjs";
|
|
2
|
+
import { newTag, castTag, toString, getStringName, findInFields } from "./calcit-data.mjs";
|
|
3
3
|
import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
|
|
4
4
|
import { CalcitStruct } from "./js-struct.mjs";
|
|
5
5
|
export class CalcitRecord {
|
|
@@ -116,6 +116,28 @@ export let new_impl_record = (impl, name, ...fields) => {
|
|
|
116
116
|
let structRef = new CalcitStruct(nameTag, fieldNames, new Array(fieldNames.length).fill(null), [impl]);
|
|
117
117
|
return new CalcitRecord(nameTag, fieldNames, undefined, structRef);
|
|
118
118
|
};
|
|
119
|
+
/** Loose record: `?{} :field1 val1 :field2 val2` – record without a declared struct.
|
|
120
|
+
* Fields are sorted by tag index. The record name is "?" (sentinel). */
|
|
121
|
+
export let _$q__$M_ = (...xs) => {
|
|
122
|
+
if (xs.length % 2 !== 0) {
|
|
123
|
+
throw new Error("?{} expected pairs of :field value");
|
|
124
|
+
}
|
|
125
|
+
let pairs = [];
|
|
126
|
+
for (let i = 0; i < xs.length; i += 2) {
|
|
127
|
+
pairs.push([castTag(xs[i]), xs[i + 1]]);
|
|
128
|
+
}
|
|
129
|
+
pairs.sort((a, b) => a[0].idx - b[0].idx);
|
|
130
|
+
// Check for duplicate fields after sorting
|
|
131
|
+
for (let i = 1; i < pairs.length; i++) {
|
|
132
|
+
if (pairs[i][0].idx === pairs[i - 1][0].idx) {
|
|
133
|
+
throw new Error(`?{} received duplicate field: :${getStringName(pairs[i][0])}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
let fieldNames = pairs.map((p) => p[0]);
|
|
137
|
+
let values = pairs.map((p) => p[1]);
|
|
138
|
+
let looseTag = newTag("?");
|
|
139
|
+
return new CalcitRecord(looseTag, fieldNames, values);
|
|
140
|
+
};
|
|
119
141
|
export let fieldsEqual = (xs, ys) => {
|
|
120
142
|
if (xs === ys) {
|
|
121
143
|
return true; // special case, referential equal
|
package/lib/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calcit/procs",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.19",
|
|
4
4
|
"main": "./lib/calcit.procs.mjs",
|
|
5
5
|
"devDependencies": {
|
|
6
6
|
"@types/node": "^25.0.9",
|
|
7
|
-
"typescript": "^
|
|
7
|
+
"typescript": "^6.0.2"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"compile": "rm -rf lib && tsc",
|
|
@@ -18,12 +18,13 @@
|
|
|
18
18
|
"test-snippets": "cargo test -q snippets::tests",
|
|
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
|
-
"check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir",
|
|
21
|
+
"check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
|
|
22
22
|
"try-rs": "cargo run --bin cr -- calcit/test.cirru",
|
|
23
23
|
"warn-dyn-method": "cargo run --bin cr -- calcit/test.cirru --warn-dyn-method",
|
|
24
24
|
"try-js-brk": "cargo run --bin cr -- calcit/test.cirru js && node --inspect-brk js-out/main.mjs",
|
|
25
25
|
"try-js": "cargo run --bin cr -- calcit/test.cirru js && node js-out/main.mjs",
|
|
26
|
-
"try-ir": "cargo run --bin cr -- calcit/test.cirru js"
|
|
26
|
+
"try-ir": "cargo run --bin cr -- calcit/test.cirru js",
|
|
27
|
+
"try-wasm": "bash scripts/test-wasm.sh"
|
|
27
28
|
},
|
|
28
29
|
"repository": {
|
|
29
30
|
"type": "git",
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calcit/procs",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.19",
|
|
4
4
|
"main": "./lib/calcit.procs.mjs",
|
|
5
5
|
"devDependencies": {
|
|
6
6
|
"@types/node": "^25.0.9",
|
|
7
|
-
"typescript": "^
|
|
7
|
+
"typescript": "^6.0.2"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"compile": "rm -rf lib && tsc",
|
|
@@ -18,12 +18,13 @@
|
|
|
18
18
|
"test-snippets": "cargo test -q snippets::tests",
|
|
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
|
-
"check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir",
|
|
21
|
+
"check-all": "yarn compile && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
|
|
22
22
|
"try-rs": "cargo run --bin cr -- calcit/test.cirru",
|
|
23
23
|
"warn-dyn-method": "cargo run --bin cr -- calcit/test.cirru --warn-dyn-method",
|
|
24
24
|
"try-js-brk": "cargo run --bin cr -- calcit/test.cirru js && node --inspect-brk js-out/main.mjs",
|
|
25
25
|
"try-js": "cargo run --bin cr -- calcit/test.cirru js && node js-out/main.mjs",
|
|
26
|
-
"try-ir": "cargo run --bin cr -- calcit/test.cirru js"
|
|
26
|
+
"try-ir": "cargo run --bin cr -- calcit/test.cirru js",
|
|
27
|
+
"try-wasm": "bash scripts/test-wasm.sh"
|
|
27
28
|
},
|
|
28
29
|
"repository": {
|
|
29
30
|
"type": "git",
|
package/ts-src/js-record.mts
CHANGED
|
@@ -119,6 +119,29 @@ export let new_impl_record = (impl: CalcitImpl, name: CalcitValue, ...fields: Ar
|
|
|
119
119
|
return new CalcitRecord(nameTag, fieldNames, undefined, structRef);
|
|
120
120
|
};
|
|
121
121
|
|
|
122
|
+
/** Loose record: `?{} :field1 val1 :field2 val2` – record without a declared struct.
|
|
123
|
+
* Fields are sorted by tag index. The record name is "?" (sentinel). */
|
|
124
|
+
export let _$q__$M_ = (...xs: Array<CalcitValue>): CalcitValue => {
|
|
125
|
+
if (xs.length % 2 !== 0) {
|
|
126
|
+
throw new Error("?{} expected pairs of :field value");
|
|
127
|
+
}
|
|
128
|
+
let pairs: Array<[CalcitTag, CalcitValue]> = [];
|
|
129
|
+
for (let i = 0; i < xs.length; i += 2) {
|
|
130
|
+
pairs.push([castTag(xs[i]), xs[i + 1]]);
|
|
131
|
+
}
|
|
132
|
+
pairs.sort((a, b) => a[0].idx - b[0].idx);
|
|
133
|
+
// Check for duplicate fields after sorting
|
|
134
|
+
for (let i = 1; i < pairs.length; i++) {
|
|
135
|
+
if (pairs[i][0].idx === pairs[i - 1][0].idx) {
|
|
136
|
+
throw new Error(`?{} received duplicate field: :${getStringName(pairs[i][0])}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
let fieldNames = pairs.map((p) => p[0]);
|
|
140
|
+
let values = pairs.map((p) => p[1]);
|
|
141
|
+
let looseTag = newTag("?");
|
|
142
|
+
return new CalcitRecord(looseTag, fieldNames, values);
|
|
143
|
+
};
|
|
144
|
+
|
|
122
145
|
export let fieldsEqual = (xs: Array<CalcitTag>, ys: Array<CalcitTag>): boolean => {
|
|
123
146
|
if (xs === ys) {
|
|
124
147
|
return true; // special case, referential equal
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|