了解 Rust 的 pest 库时接触到了这个文法。
PEG(解析表达文法)和 CFG(上下文无关文法)十分类似,不过可以确保解析没有二义性。具体而言,PEG 中的 +
*
?
这三个操作符总是贪婪匹配,选择操作符 |
总是优先尝试前面的选项,在前面的选项失败时才尝试后面的选项。因其,PEG 很适合递归向下解析。不过相比用作编译器语法分析的前端,作为正则语言的增强版应该更加合适。
比如说,PEG 模式串 a+a
无法匹配字符串 aaa
,其中的 a+
会贪婪地匹配 aaa
,导致模式串剩余的 a
无法匹配到字符。而模式串 a | ab
无法完整匹配到字符串 ab
,第一个模式 a
匹配首字符 a
匹配之后,模式串匹配成功,不会再尝试使用模式 ab
。
PEG 中另外有 &
和 !
两个操作符。&e
表示从当前位置开始匹配 e
,但是不消耗字符,也就是提前观测后面的字符串能否形成目标模式。!e
与之相反,只有在后面的字符串不能匹配 e
的时候才接着匹配,同样也不消耗字符。
让我们来看一个用文法 匹配 的例子:
S <- &(A c) 'a'+ B
A <- 'a' A? 'b'
B <- 'b' B? 'c'
其中 可以匹配 , 可以匹配 。 的规则首先使用 &(A c)
确保我们匹配得到字符串的前缀为 ,然后使用 'a'+
消耗掉所有的 ,剩下的部分使用 可以进行匹配。
熟悉 CFG 的话可以可以知道这个语言是无法用 CFG 描述(利用泵引理)的,同时我们也可以很简单地写出 PEG 无法处理而 CFG 可以处理的文法 S <- xSx | x
。因此 CFG 和 PEG 之间没有包含关系。