解析器不必复杂:bx::Scanner 的实践
Parsers don't have to be complicated

几年前,我为 bgfx 编写 shader 前端解析器时,曾尝试使用 Lemon 生成器,但生成的代码晦涩难读,最终被我弃用。另一方面,我常为小任务手写临时解析器,虽然快速无依赖,却总重复着跳过空白、收集标识符等模式,且容易埋下边界错误。我渴望一种中间方案:一套零拷贝、无分配的可复用原语,能处理扫描的重复部分,却无需引入生成器或依赖。于是诞生了 bx::Scanner。它不是解析器生成器,也不是 PEG 库,而是一个轻量级工具,通过 accept、peek 等简单操作,配合内置的行列追踪和字符类,让解析逻辑清晰可读。从 INI 解析到 URL 处理,再到堆栈符号化,它帮助我删除了第三方库依赖和多个重复的手写循环。如果你也在写指针追逐循环来跳过空白和收集标识符,不妨试试这种更优雅的方式。
如果你发现自己又在写另一个指针追逐循环来跳过空白并收集标识符,不妨考虑使用类似这样的工具。
- zabzonk
写解析器最难的点,在于从认知上接受什么会被视为有效输入。你可以写出最快、规范最完善的解析器,但总有人会以意想不到的方式(滥用)它。
著名的例子:尽管初衷良好,HTML 标签却不需要闭合;JSON 数字经常被编码成字符串;YAML 既可以是大多数人预期的样子,也可以看起来越来越像 JSON……此类情况不胜枚举。
- langbn
如果你在“临时的字节乱搞”和“解析器组合子”之间画一条线,这充其量只走了 20%。
看看链接里的 URL 解析器,为什么它不写成这样:
url = do scheme
authority
path
query
fragment
where
scheme = ...
authority = ...
etc.
它看起来完全是临时的。
- f311a
不幸的是,简单的 URL 解析会在太多东西上出错。这就是为什么每个 URL 解析库至少都有几千行代码。
测试它的一个常用方法就是传入 IPv6 URL:http://[f021:d981:b487:e57d:193e:550e::]/