在通过阅读 Stackoverflow 上关于“如何使用正则表达式解析 HTML”的问题后,我对语法学重新产生了兴趣,我抓起我的大学脚本,几分钟后我想知道我是怎么通过考试的。 作为一个简单的(好吧,我想象中的“简单”)练习,我尝试编写一个生成有效 Python 元组的上下文无关文法(CFG)(为了简单起见,只使用标识符 a、b 和 c)。经过一段时间的努力,我现在想出了这个:
G = ( {元组、元组项目、ID},{“a”、“b”、“c”、“,”、“(”、“)”},P,元组)
P 如下: 元组 → “(“ 元组项目 “)” 元组 → “(“ 元组项目 ID “)” 元组 → “(“ 元组项目 元组 “)” 元组项目 → 元组项目 元组项目 元组项目 → ID “,” 元组项目 → 元组 “,” ID → “a” ID → “b” ID → “c”
这个语法应该生成例如 (a,)、(a,b)、(a,b,)、((a,),)、((a,b),(a,),) 等,但不生成 (,a)、()、,, (a,b c) 等。我不想生成不必要的括号,如 ((a),) 或 ((a,b))。实际上,有时可选(当有多个项目时)有时必须(当只有一个项目时)的尾随逗号几乎害死我。
这个语法能生成所有有效的 Python 元组(仅使用 a、b 和 c)吗? 这个语法会生成不是有效 Python 元组的字符串吗? 这个语法是否适当? (我不确定循环标准) 为什么我的语法如此冗长?我怎样才能减少产生规则的数量?(不是通过使用管道之类的语法糖,因为它们只是将几条规则放在一行上。)
提前感谢您的评论和回答。
2、解决方案
第一个答案:
在不实际引用 Python 语法的情况下,我相当肯定您的语法生成了所有有效的 Python 元组,除了一个 (()),即空元组,并且它不会生成任何不是 Python 元组的内容。所以在这种程度上,它是好的。 但是,它对解析没有太大用处,因为
元组项目 → 元组项目 元组项目
是呈指数级不明确的。(顺便说一下,元组项目不是这个非终结符的非常描述性名称,它实际上是一个列表。)不明确的语法在某种意义上是“适当的”,因为它们遵守上下文无关语法的所有规则,但明确的语法通常更好。 很容易修复:
元组 → “(“ “)” 元组 → “(“ 项目列表 “,” “)” 元组 → “(“ 项目列表 “,” 项目 “)” 项目列表 → 项目 项目列表 → 项目列表 “,” 项目 项目 → ID 项目 → 元组
(我省略了 ID 生成;在实用语法中,ID 将是一个终结符,但这几乎没有区别。) 最后,为什么这个语法“如此冗长”? (七个产品真的“如此冗长”吗?取决于您的标准,我想。)简单的答案是 CFG 就是这样的。您可以在右侧添加语法糖,使之成为正则表达式(不仅是交替,还有 Kleene 星及其伴随):
元组 → “(“ [ 项目列表 “,” 项目? ]? “)” 项目列表 → 项目 // “,” 项目 → ID | 元组
在这里我使用了有用的内插运算符 //,这在学术课程中很少被教授,因此令人惊讶的是很少有实现:
a // b =def a(ba)*
上面的内容是否更容易阅读,我留给读者。它类似于语法说明中常用的扩展巴科斯范式 (EBNF),尤其是在 RFC 中。(EBNF 是少数具有插值运算符的形式主义之一,尽管它不像我的那样明确地写。)
无论如何,除此之外,我不相信您的语法可以被修剪。