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

272 天前
 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 正则表达式

2310 次点击
所在节点    分享创造
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1028280

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX