diff --git a/Agents.md b/Agents.md new file mode 100644 index 0000000..1da34a0 --- /dev/null +++ b/Agents.md @@ -0,0 +1,75 @@ +# bisection-key Agent 操作手册 + +本文件用于记录高频开发操作,优先面向日常改动、验证与提交流程。 + +## 1. 修改 `compact.cirru` 的硬规则 + +- 先执行:`cr docs agents --full` +- 不直接手改 `compact.cirru`,统一使用: + - `cr query` 查看定义/结构 + - `cr edit` 修改定义、schema、import + - `cr tree` 做局部节点级改动 + +## 2. 高频查询命令 + +```bash +cr query ns +cr query defs bisection-key.core +cr query defs bisection-key.util +cr query peek bisection-key.core/bisect +cr query def bisection-key.core/bisect-vec +cr query find --deps +``` + +## 3. 高频编辑命令 + +```bash +# 更新定义 +cr edit def bisection-key.core/dictionary --overwrite -e 'def dictionary |+-/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + +# 更新函数 schema +cr edit schema bisection-key.util/key-after -e ':: :fn ({} (:args ([] :map :string)) (:return :string))' + +# 处理包含 ? 的函数名时要加引号 +cr edit schema 'bisection-key.util/has-nth?' -e ':: :fn ({} (:args ([] :map :number)) (:return :bool))' +``` + +## 4. 本地验证流程 + +```bash +# 静态检查 +cr --check-only + +# 解释执行测试 +yarn test:cr + +# JS 产物测试 +yarn test:js + +# WASM 编译冒烟 +yarn test:wasm:compile + +# WASM 运行断言(当前用于暴露 API 逻辑偏差) +yarn test:wasm +``` + +说明:`test:wasm` 当前是“运行时断言测试”,用于反映 WASM 下 API 是否真的行为正确,不等同于仅编译通过。 + +## 5. 提交与 PR + +```bash +git status --short +git add -A +git commit -m "" +git push origin wasm-support +gh pr view 9 --json title,url,state +gh pr checks 9 +``` + +如果 `gh pr checks` 临时失败,优先检查网络连通性后重试。 + +## 6. 当前已知重点 + +- `bisection-key.wasm-probe` 里有大量 probe,可用于定位 WASM 运行时行为偏差。 +- `dictionary`/字符串初始化路径是 WASM 重点排查点。 +- `compact.cirru` 改动后,先跑 `cr --check-only` 再跑 `yarn test:cr`/`yarn test:js`。 \ No newline at end of file diff --git a/README.md b/README.md index a11f42d..a00c781 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,26 @@ cr # run and watch For JavaScript: ```bash -cr --emit-js -1 # emit-js once -yarn build -node js-out/bundle.js # run code +cr js -1 +node main.mjs ``` +For tests: + +```bash +yarn test:cr +yarn test:js +``` + +For WASM: + +```bash +yarn test:wasm:compile +yarn test:wasm +``` + +`test:wasm` runs runtime assertions for probe APIs and will fail when WASM runtime behavior diverges from expected API semantics. + ### Special cases Nothing could be inserted between `a` and `a+` since `+` is very close to zero. Such a key which ends with `+` should not be created from current implementation. diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 315f3c2..0000000 --- a/TODO.md +++ /dev/null @@ -1,116 +0,0 @@ -# bisection-key WASM 支持 TODO - -本文件记录 WASM 编译目标的当前状态与后续工作计划。 - ---- - -## 当前状态(2026-04-22) - -### 已完成 - -- ✅ 升级 `deps.cirru` calcit-version → `0.12.25` -- ✅ 升级 `package.json`:`@calcit/procs ^0.12.25`,`packageManager: yarn@4.12.0`,新增 `test:cr` / `test:js` / `test:wasm` 脚本 -- ✅ 添加 `.yarnrc.yml`(`nodeLinker: node-modules`) -- ✅ 升级 CI workflow:`setup-node@v6`,node 24,Corepack + yarn@4.12.0,`caps --ci && yarn install --immutable` -- ✅ 为 `bisection-key.core` 核心函数添加 schema 类型标注(`:string`、`:number`、`:bool`): - - `bisect (x :string y :string) :string` - - `bisect-vec (result :string xs0 :string ys0 :string idx :number) :string` - - `lookup-i (c :string) :number` - - `peek-tiny? (x :dynamic) :bool` - - `trim-right (x :string) :string` -- ✅ 新增 `bisection-key.wasm-probe` 命名空间,含 3 个 probe 函数: - - `probe-bisect-basic`:验证 5 个基础 bisect 结果 - - `probe-bisect-strings`:验证字符串边界 bisect 结果 - - `probe-all-count`:返回总通过数,作为 WASM 冒烟测试的入口 -- ✅ WASM 编译命令(无需修改 `:entries`,直接通过 `--init-fn` 指定): - ```bash - cr-wasm compact.cirru --init-fn bisection-key.wasm-probe/probe-all-count - ``` - -### 待解决:`dictionary` 全局 def 在 WASM 中为空 - -**症状**:`probe-dictionary` 返回 `0`(count = 0),`bisect` 调用链内部触发 WASM `unreachable` trap。 - -**根因分析**: -- `dictionary` 定义为 `def dictionary $ str |+-/ |0123456789 |ABCDEFGHIJKLMNOPQRSTUVWXYZ |abcdefghijklmnopqrstuvwxyz` -- `str` 内联调用本身没问题(`probe-str-inline` 验证:相同参数返回 65 字符 ✅) -- 但作为全局 `def` 初始化时,WASM codegen 可能不支持在**模块初始化阶段**调用 `str` 等运行时函数 -- WASM 线性内存的全局变量初始化(`global.set`)只允许常量表达式,调用 host runtime 函数会被忽略或截断 - -**调试线索**: -- `probe-str-inline` 内联 `str` 调用 → 返回 65 ✅ -- `probe-dictionary` 读全局 `def dictionary` → 返回 0 ❌ -- 说明问题是全局 `def` 的初始化时机,不是 `str` 函数本身 - ---- - -## 后续工作计划 - -### P1:修复全局 `def` 动态初始化 - -**方案**:在 `cr-wasm` 的代码生成中,对非常量的全局 `def`(即初始化值中含有函数调用)改为**延迟初始化**: - -1. 在 WASM 模块中用 `global` 节存储 `i32`(指针),初始为 `0`(null) -2. 在导出的 `__init` / `__wasm_start` 函数中,按拓扑顺序依次求值并 `global.set` -3. 调用任何导出函数前,JS 侧先调用 `__init()` - -或者更简单的方案:在 `emit_wasm.rs` 的全局 def 处理逻辑中,检测到非常量初始化时,将其改为在**第一次被读取时(lazy)**求值并缓存(惰性初始化),使用一个 `i32` flag bit 标记是否已初始化。 - -### P2:补充类型标注以提升静态路径覆盖率 - -以下函数尚无 schema 标注,可按需补充: - -- `bisection-key.util/key-after`、`key-before`、`key-append`、`key-prepend` -- `bisection-key.util/assoc-*` 系列 -- `bisection-key.util/key-nth`、`val-nth`、`key-index-of` - -### P3:添加 WASM 专用测试脚本 - -创建 `wasm-test.mjs`,结构参考 `recollect/scripts/run-wasm-api.sh`: - -```js -// wasm-test.mjs -import { readFileSync } from 'fs'; -const mod = new WebAssembly.Module(readFileSync('js-out/program.wasm')); -const inst = new WebAssembly.Instance(mod, { math: {...}, io: {...} }); -const e = inst.exports; - -// 调用 __init 初始化全局 def(P1 修复后启用) -// e['__init']?.(); - -const score = e['probe-all-count'](); -const expected = 7; // probe-bisect-basic(5) + probe-bisect-strings(2) -if (score !== expected) { - console.error(`WASM probe-all-count: expected ${expected}, got ${score}`); - process.exit(1); -} -console.log(`=== bisection-key WASM probe passed: ${score}/${expected} ===`); -``` - -### P4:在 CI 中加入 WASM 步骤(依赖 P1 完成) - -在 `.github/workflows/tests.yaml` 补充: - -```yaml -- name: "test wasm" - run: cr-wasm compact.cirru --init-fn bisection-key.wasm-probe/probe-all-count && node wasm-test.mjs -``` - ---- - -## 编译验证命令速查 - -```bash -# 升级依赖 -caps && yarn install --immutable - -# 运行 cr 解释器测试 -cr --entry test -1 - -# 编译 JS 并测试 -cr --entry test js -1 && node test.mjs - -# 编译 WASM(需要本地 cr-wasm binary) -cr-wasm compact.cirru --init-fn bisection-key.wasm-probe/probe-all-count -node wasm-test.mjs # P3 完成后可用 -``` diff --git a/compact.cirru b/compact.cirru index f478e62..24fe5d1 100644 --- a/compact.cirru +++ b/compact.cirru @@ -30,7 +30,7 @@ raise $ str "|unexpected identical ids: " xs0 "| " ys0 (&>= idx (&str:count xs0)) let - c-y $ &str:nth ys0 idx + c-y $ str-nth ys0 idx if (&= c0 c-y) if &= (inc idx) (&str:count ys0) @@ -38,33 +38,33 @@ recur (str result c0) xs0 ys0 $ inc idx if (&= c1 c-y) if - peek-tiny? $ &str:nth ys0 (inc idx) + peek-tiny? $ str-nth ys0 (inc idx) str result c0 c32 str result c-y - str result $ &str:nth dictionary + str result $ str-nth dictionary bit-shr (lookup-i c-y) 1 (&>= idx (&str:count ys0)) let - c-x $ &str:nth xs0 idx + c-x $ str-nth xs0 idx if (&= c-x c64) if &= (inc idx) (&str:count xs0) - str result c64 $ &str:nth dictionary 16 + str result c64 $ str-nth dictionary 16 recur (str result c64) xs0 ys0 $ inc idx case-default c-x - str result $ &str:nth dictionary + str result $ str-nth dictionary bit-shr &+ &* 3 $ lookup-i c-x , 64 , 2 c63 $ str result c64 - (&str:nth dictionary 62) (str result c63) - (&str:nth dictionary 61) - str result $ &str:nth dictionary 62 + (str-nth dictionary 62) (str result c63) + (str-nth dictionary 61) + str result $ str-nth dictionary 62 true $ let - c-x $ &str:nth xs0 idx - c-y $ &str:nth ys0 idx + c-x $ str-nth xs0 idx + c-y $ str-nth ys0 idx x $ lookup-i c-x y $ lookup-i c-y delta $ &- y x @@ -74,45 +74,45 @@ recur (str result c-x) xs0 ys0 $ inc idx (&= delta 1) if - peek-tiny? $ &str:nth ys0 next + peek-tiny? $ str-nth ys0 next if &= next $ &str:count xs0 str result c-x c32 if - &= (&str:nth xs0 next) c64 + &= (str-nth xs0 next) c64 recur (str result c-x) xs0 | next - str result c-x $ &str:nth dictionary + str result c-x $ str-nth dictionary bit-shr &+ - lookup-i $ &str:nth xs0 next + lookup-i $ str-nth xs0 next , 65 , 1 str result c-y true $ str result - &str:nth dictionary $ bit-shr (&+ x y) 1 + str-nth dictionary $ bit-shr (&+ x y) 1 :examples $ [] :schema $ :: :fn {} (:return :string) :args $ [] :string :string :string :number |c0 $ %{} :CodeEntry (:doc |) (:schema :string) :code $ quote - def c0 $ &str:nth dictionary 0 + def c0 $ str-nth dictionary 0 :examples $ [] |c1 $ %{} :CodeEntry (:doc |) (:schema :string) :code $ quote - def c1 $ &str:nth dictionary 1 + def c1 $ str-nth dictionary 1 :examples $ [] |c32 $ %{} :CodeEntry (:doc |) (:schema :string) :code $ quote - def c32 $ &str:nth dictionary 32 + def c32 $ str-nth dictionary 32 :examples $ [] |c63 $ %{} :CodeEntry (:doc |) (:schema :string) :code $ quote - def c63 $ &str:nth dictionary 63 + def c63 $ str-nth dictionary 63 :examples $ [] |c64 $ %{} :CodeEntry (:doc |) (:schema :string) :code $ quote - def c64 $ &str:nth dictionary 64 + def c64 $ str-nth dictionary 64 :examples $ [] |char->int-map $ %{} :CodeEntry (:doc |) (:schema :dynamic) :code $ quote @@ -121,8 +121,7 @@ pairs-map :examples $ [] |dictionary $ %{} :CodeEntry (:doc |) (:schema :string) - :code $ quote - def dictionary $ str |+-/ |0123456789 |ABCDEFGHIJKLMNOPQRSTUVWXYZ |abcdefghijklmnopqrstuvwxyz + :code $ quote (def dictionary |+-/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz) :examples $ [] |lookup-i $ %{} :CodeEntry (:doc |) :code $ quote @@ -191,6 +190,17 @@ :schema $ :: :fn {} (:return :string) :args $ [] + |str-nth $ %{} :CodeEntry (:doc |) + :code $ quote + defn str-nth (s idx) + if + &< idx $ &str:count s + &str:nth s idx + , nil + :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :string :number |trim-right $ %{} :CodeEntry (:doc |) :code $ quote defn trim-right (x) @@ -241,15 +251,21 @@ recur (inc i) new-id , x :examples $ [] - |main! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |main! $ %{} :CodeEntry (:doc |) :code $ quote defn main! () (run-bisection!) (println "|App started.") :examples $ [] - |reload! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :unit) + :args $ [] + |reload! $ %{} :CodeEntry (:doc |) :code $ quote defn reload! () (run-bisection!) (println "|Code updated.") :examples $ [] - |run-bisection! $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :unit) + :args $ [] + |run-bisection! $ %{} :CodeEntry (:doc |) :code $ quote defn run-bisection! () (; compare-random-ids) (; list-appending-results) ; println $ bisect |yyyz |z @@ -274,6 +290,9 @@ recur (inc i) new-id , nil :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :ns $ %{} :NsEntry (:doc |) :code $ quote ns bisection-key.main $ :require @@ -451,14 +470,17 @@ bisection-key.util :refer $ key-before key-after assoc-before assoc-after key-prepend key-append assoc-prepend assoc-append get-min-key get-max-key key-nth val-nth assoc-nth assoc-before-nth assoc-after-nth key-index-of |bisection-key.util $ %{} :FileEntry :defs $ {} - |assoc-after $ %{} :CodeEntry (:doc |) (:schema :dynamic) + |assoc-after $ %{} :CodeEntry (:doc |) :code $ quote defn assoc-after (dict base-key v) let new-key $ key-after dict base-key assoc dict new-key v :examples $ [] - |assoc-after-nth $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :map) + :args $ [] :map :string :dynamic + |assoc-after-nth $ %{} :CodeEntry (:doc |) :code $ quote defn assoc-after-nth (x n v) when-not (has-nth? x n) (raise "|Succeeded map size") @@ -466,7 +488,10 @@ k $ key-nth x n assoc-after x k v :examples $ [] - |assoc-append $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :map) + :args $ [] :map :number :dynamic + |assoc-append $ %{} :CodeEntry (:doc |) :code $ quote defn assoc-append (dict v) assert (map? dict) "|dict should be a map" @@ -474,14 +499,20 @@ k $ key-append dict assoc dict k v :examples $ [] - |assoc-before $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :map) + :args $ [] :map :dynamic + |assoc-before $ %{} :CodeEntry (:doc |) :code $ quote defn assoc-before (dict base-key v) let new-key $ key-before dict base-key assoc dict new-key v :examples $ [] - |assoc-before-nth $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :map) + :args $ [] :map :string :dynamic + |assoc-before-nth $ %{} :CodeEntry (:doc |) :code $ quote defn assoc-before-nth (x n v) when-not (has-nth? x n) (raise "|Succeeded map size") @@ -489,7 +520,10 @@ k $ key-nth x n assoc-before x k v :examples $ [] - |assoc-nth $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :map) + :args $ [] :map :number :dynamic + |assoc-nth $ %{} :CodeEntry (:doc |) :code $ quote defn assoc-nth (x n v) when-not (has-nth? x n) (raise "|Succeeded map size") @@ -497,7 +531,10 @@ k $ key-nth x n assoc x k v :examples $ [] - |assoc-prepend $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :map) + :args $ [] :map :number :dynamic + |assoc-prepend $ %{} :CodeEntry (:doc |) :code $ quote defn assoc-prepend (dict v) assert (map? dict) "|dict should be a map" @@ -505,26 +542,38 @@ k $ key-prepend dict assoc dict k v :examples $ [] - |get-max-key $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :map) + :args $ [] :map :dynamic + |get-max-key $ %{} :CodeEntry (:doc |) :code $ quote defn get-max-key (x) last $ sort &set:to-list $ keys x , &compare :examples $ [] - |get-min-key $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :map + |get-min-key $ %{} :CodeEntry (:doc |) :code $ quote defn get-min-key (x) &list:first $ sort &set:to-list $ keys x , &compare :examples $ [] - |has-nth? $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :map + |has-nth? $ %{} :CodeEntry (:doc |) :code $ quote defn has-nth? (x n) &< n $ count x :examples $ [] - |key-after $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :bool) + :args $ [] :map :number + |key-after $ %{} :CodeEntry (:doc |) :code $ quote defn key-after (dict base-key) assert (string? base-key) "|base-key should be string" @@ -540,7 +589,10 @@ , max-id &list:nth existing-keys $ inc position :examples $ [] - |key-append $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :string) + :args $ [] :map :string + |key-append $ %{} :CodeEntry (:doc |) :code $ quote defn key-append (dict) assert (map? dict) "|dict should be a map" @@ -548,7 +600,10 @@ &set:max $ keys dict , max-id :examples $ [] - |key-before $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :string) + :args $ [] :map + |key-before $ %{} :CodeEntry (:doc |) :code $ quote defn key-before (dict base-key) assert (string? base-key) "|base-key should be string" @@ -563,7 +618,10 @@ if (= 0 position) min-id $ get existing-keys (dec position) , base-key :examples $ [] - |key-index-of $ %{} :CodeEntry (:doc "|find index of `k`, returns `nil` if not found") (:schema :dynamic) + :schema $ :: :fn + {} (:return :string) + :args $ [] :map :string + |key-index-of $ %{} :CodeEntry (:doc "|find index of `k`, returns `nil` if not found") :code $ quote defn key-index-of (x k) let @@ -572,7 +630,10 @@ , &compare index-of ks k :examples $ [] - |key-nth $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :map :string + |key-nth $ %{} :CodeEntry (:doc |) :code $ quote defn key-nth (x n) if (has-nth? x n) @@ -583,20 +644,29 @@ , n , nil :examples $ [] - |key-prepend $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :map :number + |key-prepend $ %{} :CodeEntry (:doc |) :code $ quote defn key-prepend (dict) assert (map? dict) "|dict should be a map" if (empty? dict) mid-id $ bisect min-id &set:min $ keys dict :examples $ [] - |val-nth $ %{} :CodeEntry (:doc |) (:schema :dynamic) + :schema $ :: :fn + {} (:return :string) + :args $ [] :map + |val-nth $ %{} :CodeEntry (:doc |) :code $ quote defn val-nth (x n) if (has-nth? x n) get x $ key-nth x n do (println "|[Warn] exceeded map size") nil :examples $ [] + :schema $ :: :fn + {} (:return :dynamic) + :args $ [] :map :number :ns $ %{} :NsEntry (:doc |) :code $ quote ns bisection-key.util $ :require @@ -824,7 +894,7 @@ |probe-dictionary $ %{} :CodeEntry (:doc |) :code $ quote defn probe-dictionary () $ if - > (count dictionary) 0 + &> (&str:count dictionary) 0 , 1 0 :examples $ [] :schema $ :: :fn @@ -870,6 +940,20 @@ :schema $ :: :fn {} (:return :bool) :args $ [] + |probe-find-index-literal-1 $ %{} :CodeEntry (:doc |) + :code $ quote + defn probe-find-index-literal-1 () $ &str:find-index |+-/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz |1 + :examples $ [] + :schema $ :: :fn + {} (:return :number) + :args $ [] + |probe-find-index-literal-plus $ %{} :CodeEntry (:doc |) + :code $ quote + defn probe-find-index-literal-plus () $ &str:find-index |+-/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz |+ + :examples $ [] + :schema $ :: :fn + {} (:return :number) + :args $ [] |probe-inc0 $ %{} :CodeEntry (:doc |) :code $ quote defn probe-inc0 () $ inc 0 @@ -940,6 +1024,24 @@ :schema $ :: :fn {} (:return :number) :args $ [] + |probe-nth-literal-0 $ %{} :CodeEntry (:doc |) + :code $ quote + defn probe-nth-literal-0 () $ if + &= (&str:nth |+-/ 0) |+ + , 1 0 + :examples $ [] + :schema $ :: :fn + {} (:return :number) + :args $ [] + |probe-nth-literal-1 $ %{} :CodeEntry (:doc |) + :code $ quote + defn probe-nth-literal-1 () $ if + &= (&str:nth |+-/ 1) |- + , 1 0 + :examples $ [] + :schema $ :: :fn + {} (:return :number) + :args $ [] |probe-str-3arg $ %{} :CodeEntry (:doc |) :code $ quote defn probe-str-3arg () $ str |a |b |c diff --git a/deps.cirru b/deps.cirru index 3c5ffc8..32acd93 100644 --- a/deps.cirru +++ b/deps.cirru @@ -1,3 +1,3 @@ -{} (:calcit-version |0.12.25) +{} (:calcit-version |0.12.29) :dependencies $ {} (|calcit-lang/calcit-test |0.0.6) diff --git a/package.json b/package.json index d55cea7..d552ee5 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,12 @@ "version": "0.0.16", "packageManager": "yarn@4.12.0", "dependencies": { - "@calcit/procs": "^0.12.25" + "@calcit/procs": "^0.12.29" }, "scripts": { "test:cr": "cr --entry test", "test:js": "cr --entry test js && node test.mjs", - "test:wasm": "cr --entry wasm wasm && node wasm-test.mjs" + "test:wasm:compile": "cr-wasm compact.cirru --init-fn bisection-key.wasm-probe/probe-all-count", + "test:wasm": "yarn test:wasm:compile && node wasm-test.mjs" } } diff --git a/wasm-test.mjs b/wasm-test.mjs new file mode 100644 index 0000000..5d8ab65 --- /dev/null +++ b/wasm-test.mjs @@ -0,0 +1,67 @@ +import { readFileSync } from 'node:fs'; + +const wasmPath = 'js-out/program.wasm'; + +let e; + +try { + const mod = new WebAssembly.Module(readFileSync(wasmPath)); + const inst = new WebAssembly.Instance(mod, { + math: { + pow: Math.pow, + sin: Math.sin, + cos: Math.cos, + }, + io: { + log_value: () => 0, + }, + }); + + e = inst.exports; +} catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`[wasm-test] failed to load or instantiate ${wasmPath}: ${message}`); + process.exit(1); +} + +const checks = [ + ['probe-find-index-literal-plus', 0], + ['probe-find-index-literal-1', 4], + ['probe-nth-literal-0', 1], + ['probe-nth-literal-1', 1], + ['probe-dictionary', 1], + ['probe-bisect-basic', 5], + ['probe-bisect-strings', 2], + ['probe-all-count', 7], +]; + +let failed = false; + +for (const [name, expected] of checks) { + const fn = e[name]; + if (typeof fn !== 'function') { + console.error(`[wasm-test] missing export: ${name}`); + failed = true; + continue; + } + + try { + const value = fn(); + if (value !== expected) { + console.error(`[wasm-test] ${name} expected ${expected}, got ${value}`); + failed = true; + } else { + console.log(`[wasm-test] ${name} ok: ${value}`); + } + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error(`[wasm-test] ${name} threw: ${message}`); + failed = true; + } +} + +if (failed) { + process.exitCode = 1; +} else { + console.log('[wasm-test] all probe checks passed'); +} diff --git a/yarn.lock b/yarn.lock index 5c05374..090a6e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,14 +5,14 @@ __metadata: version: 8 cacheKey: 10c0 -"@calcit/procs@npm:^0.12.25": - version: 0.12.25 - resolution: "@calcit/procs@npm:0.12.25" +"@calcit/procs@npm:^0.12.29": + version: 0.12.29 + resolution: "@calcit/procs@npm:0.12.29" dependencies: "@calcit/ternary-tree": "npm:0.0.25" "@cirru/parser.ts": "npm:^0.0.9" "@cirru/writer.ts": "npm:^0.1.7" - checksum: 10c0/49c3b2e97e62c30502b8dc3ed6a145cdfd23551826433bcaee92921d72b95414d2fa1296ced0270dc4e17d6b7de39c896577bfdc1e072e3672ebbb077c649426 + checksum: 10c0/f608f4501f097bf37d14d6d0db1bc02c83b44d4293a3c7af7087444d730238110ddeff2a8096b04bbd2766da3798858496c296a6704601c53ccb9dcae343f7ff languageName: node linkType: hard @@ -41,6 +41,6 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - "@calcit/procs": "npm:^0.12.25" + "@calcit/procs": "npm:^0.12.29" languageName: unknown linkType: soft