直觉理解 Pratt 解析

原文 HN 讨论

文章摘要

这篇文章旨在帮助读者建立对 Pratt 解析(Pratt Parsing,也称为”自顶向下运算符优先解析”)的直觉理解。Pratt 解析是一种优雅而高效的表达式解析技术,由 Vaughan Pratt 在 1973 年提出,至今仍被广泛应用于编程语言的编译器和解释器中。

传统的表达式解析方法通常依赖 BNF(巴科斯-诺尔范式)语法规则,为每个运算符优先级定义不同的语法规则层级。例如,要正确解析 1 + 2 * 3,需要定义加法表达式和乘法表达式的层级关系。这种方法虽然直观,但当运算符优先级增多时(如 SystemVerilog 有 16 个优先级),代码会变得冗长且难以维护。

Pratt 解析的核心思想是”绑定力”(binding power)。每个运算符被赋予一个数值表示其绑定力(即优先级),解析器通过比较当前运算符与上下文中的绑定力来决定如何组合表达式。绑定力越高的运算符越”紧密”地绑定其操作数。

解析过程的直觉理解可以这样建立:想象解析中缀表达式时,算法在遇到每个运算符时都会问一个简单的问题——”这个运算符的绑定力是否足够强,能够’抢走’左边已经解析的表达式?”如果是,它就继续向右解析;如果不是,它就返回当前的结果,让调用者(绑定力更低的运算符)来处理。

这种方法的美妙之处在于:它只需要一个简单的循环和递归调用,加上一个绑定力查找表,就能正确处理任意数量的运算符优先级和结合性。前缀运算符(如负号 -x)通过 “nud”(null denotation,空符号)函数处理,中缀运算符(如加法 x + y)通过 “led”(left denotation,左符号)函数处理。

文章还介绍了几个相关的解析算法:优先级攀升(precedence climbing)本质上与 Pratt 解析等价,只是组织方式略有不同;调度场算法(shunting yard)是 Pratt 解析的迭代变体,使用显式栈而非递归。作者引导读者从简单的全括号表达式开始,逐步移除括号并引入优先级概念,最终自然地推导出 Pratt 解析的逻辑。

对于编译器和语言设计爱好者来说,Pratt 解析是一个”知道就无法忘记”的技术——它将复杂的语法解析问题简化为一个直观的数值比较问题。

HN 评论精华

对 Pratt 解析的赞誉

学术方法 vs 实践方法的辩论

实现方式的讨论

核心直觉的解释