分享一种借助 Kotlin/Wasm 在 JavaScript 中使用一致的 Kotlin/ Java 正则表达式的方案

查看 11|回复 0
作者:lisongeee   
为什么会有这个需求
起因是我给我自己编写的 高级选择器 增加了 ~= 操作符(等价 Kotlin/Java 里的 String.mtches 函数)
比如 [text~='ikun?'] 简单匹配 text 属性为 ikun 或者 iku 的节点
这个代码需要同时在 Android 和 浏览器 上运行,源代码是基于 Kotlin Multiplatform 构建,可以直接编译到 JavaScript
但是涉及到正则表达式,它在 Kotlin/Js 这块的 Regex 对象只是对 JavaScript 的 RegExp 的简单封装,缺失了很多特性,并没有真正实现跨平台一致
比如 (?im)abc 这个正则表达式在 Kotlin/Jvm 上被正常编译,但 RegExp 不支持此正则语法,所以在 Kotlin/Js 上运行后提示非法正则
也可以在 KT-49065 找到类似的问题,所以为了保持一致性,我需要解决在 JavaScript 上使用的问题
解决此问题的历程
我一开始想着看看有没有人用 JavaScript 手动实现 Kotlin/Java 正则表达式,有的话我直接 pnpm add 就完了
搜遍了 www.npmjs.com 果然没有,看来是这个需求太小众了么
后面我又找到 regex101.com,看起来它在网页上实现了 Java8 的正则表达式解析和匹配
可惜它不是开源的,在 Github 找到的相关代码也是编译压缩后的产物
然后我找到了 openjdk17 的 java/util/regex/Pattern.java 的源代码,想着手动转译实现吧
但是看着如此庞大的代码,我一个 BUG 制造机器还是放弃了
最后想到了 Kotlin Multiplatform 下的 Kotlin/Wasm (目前处于实验阶段)
简单的原理描述就是将 kotlin 代码编译到 wasm 导出对象然后通过在 浏览器 端加载替换匹配函数
1 . 编译 导出函数到 wasm
@JsExport
fun toMatches(source: String): (input: String) -> Boolean {
    val regex = Regex(source)
    return { input -> regex.matches(input) }
}
2 . 在浏览器 加载 这个导出函数并执行替换
import matchesInstantiate from '@gkd-kit/wasm_matches';
import matchesWasmUrl from '@gkd-kit/wasm_matches/dist/mod.wasm?url';
matchesInstantiate(fetch(matchesWasmUrl))
  .then((mod) => {
    const toMatches = mod.exports.toMatches;
    updateWasmToMatches(toMatches);
  })
关键流程就两步,看起来其实也挺简单的
说一下 Kotlin/Wasm 需要浏览器支持 WasmGC,也就是版本需要满足下列条件

如果你的浏览器不满足条件,会有一个提示弹窗并自动回退到原来的 JavaScript RegExp 实现
实际体验
你可以在 https://i.gkd.li/i/14045424 使用选择器查询 [text~=".*[0-9].*"] 找到属性 text 包含数字的所有节点
如果你的浏览器版本符合上面说的,那么你能在上述网页里体验到完整的 Kotlin/Java 正则表达式
您需要登录后才可以回帖 登录 | 立即注册

返回顶部