起因是我给我自己编写的 高级选择器 增加了 ~=
操作符(等价 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 正则表达式