告别 regex:Parser Combinators 的优雅解析

解析是将无序文本转化为结构化数据的关键步骤。虽然 regex 是常见工具,但在处理复杂任务时往往显得混乱不堪;而 tree-sitter 又常显过度。Parser Combinators 恰好填补了这两者之间的空白。通过定义 Partial Parser,我们可以将输入字符串切割并返回结果与剩余部分。借助 pSeq 等组合器,能够将多个简单的解析器链式调用,构建出强大的解析逻辑。从解析简单的问候语到处理复杂的 PING 命令输出,Parser Combinators 展示了如何通过组合小单元来优雅地解决大问题,让代码更清晰、更易维护。
Parser Combinators 完美填补了 regex 在处理复杂任务时的混乱与 tree-sitter 的过度之间的空白。
HN 评论区
88- BoppreH
如果你在用 Python,且问题规模大到单个 regex 搞不定,但又小到用不上文中那种完整的解析器,我推荐内置的 re.Scanner[0]。它是 `re` 模块里基于 regex 的 tokenizer[1](所以不支持递归解析),出于某些原因,官方文档里完全没提它。
你只需为每种 token 类型提供一个模式和一个处理匹配项的函数,就能得到处理后的 token 列表。
重要的是,它单遍处理列表并确保匹配项是连续的,而使用捕获组的 naive `re.findall` 会忽略未匹配字符。此外,你还能获得对运行中 scanner 的引用,从而可以记录匹配位置以便报错。
import re
# 初始化 scanner。
scanner = re.Scanner([
(r"\d+", lambda scanner, token: ('COUNT', int(token))),
(r"\w+", lambda scanner, token: ('ANIMAL', token.removesuffix('s'))),
(r"[,. ]", None), # None -> 跳过该 token。
])
# 扫描示例字符串。
results, remainder = scanner.scan("45 pigeons, 23 cows, 11 spiders.")
# 确保解析了全部内容。
assert not remainder
print(results)
# [('COUNT', 45),
# ('ANIMAL', 'pigeon'),
# ('COUNT', 23),
# ('ANIMAL', 'cow'),
# ('COUNT', 11),
# ('ANIMAL', 'spider')]
[0]: https://stackoverflow.com/a/693818/252218
[1]: https://en.wikipedia.org/wiki/Lexical_analysis#Tokenization
- ArneCode
我非常喜欢这些动画,让理解正在发生的事情变得直观!
我认为 parser combinators 的一个缺点(例如与 parser generator 库相比,后者是从单独的语法文件生成解析器),是你需要添加大量的映射/解包代码。在我看来,这让解析器更难阅读。
为了改进这一点,我正在开发一个 Rust 的 parser combinator 库。
它增加了两个额外的 combinator(capture 和 bind),可以将解析器的结果存储在桶中(bind),并在稍后使用这些桶中存储的值来构建结果(capture)。
这允许你消除解包/映射代码,并将语法与结果构建分离开来。
如果你有时间,我非常欢迎反馈!
你可以在这里找到它:https://github.com/ArneCode/marser
- marginalia_nu
我不明白为什么不能做成单页。当你随着获取新信息需要回溯前面的“章节”时,幻灯片形式真的很难参考。而且也无法搜索。
看起来我们出于奇怪的审美原因,在数字媒体中没必要地重新引入了纸质媒介的限制?
- akst
我觉得这个网站真的很酷,也很喜欢这个话题。
但我觉得你可能需要在小屏幕上测试一下这个页面,感兴趣的内容可用屏幕空间非常有限:右上角留了大量空白,可滚动内容基本上只占可用屏幕空间的 1/3,左侧又占了大约 1/3。
我知道这是你的网站、你的愿景,我理解并尊重你在 UI 上尝试不同的做法。但如果你这里的目标是传达你的想法,作为读者,我会更希望有更多屏幕空间留给内容。当我点击链接来看这篇特定帖子时,我对你的其他帖子并不感兴趣(或者至少在我读完这篇特意点进来看的帖子之前不感兴趣)。
顺便一提,我现在用的是 MacBook Air。
- boomlinde
第一个地址可能不应该被解析为 IP 地址,因为它也可能是主机名。这看起来像是在吹毛求疵,但它提供了一个机会,可以演示当你预期出现多种构造之一时该怎么做。
由于正文文本所占区域有限,这篇文章读起来有点费劲。尽管章节很短,我还是不得不滚动好几页。