每个本科生编写入门课程都会回顾常用的无上下文语法子集:LL(k),SLR(k),LALR(k),LR(k)。我们还被教导,对于任何给定的k,每个语法都是下一个语法的子集。
我从未见过的是解释什么样的编程语言语法特征可能需要转移到不同的语言类。 GLR解析器有一个明显的实际动机,即在解析C ++时避免解析器和符号表的不合理混合。但是,LL和LR这两个“标准”类之间的差异呢?
两个问题:
通过使k尽可能小来减少语言能力有一个似是而非的理由,因为需要许多许多前瞻标记的语言对于人类来说难以解析,以及机器要解析的“更难”。问题(2)隐含地询问相同的推理是否最终在类之间以及在类中保持。
编辑:这里有一个例子来说明我正在寻找的各种答案,但对于常规语言而不是无上下文:
在描述常规语言时,通常会有三个运算符:+
,*
和?
。现在,您可以在不降低语言功能的情况下删除+
;而不是写x+
,你写xx*
,效果是一样的。但是如果x
是一个庞大而多毛的表达,那么由于人类的遗忘,两个x
可能会随着时间的推移而发散,产生一个与原作者的意图不符的语法正确的正则表达式。因此,即使添加+
并不严格增加功率,它也会使符号更不容易出错。
当从LR切换到LL时,是否存在具有类似实际(人类?)效果的构造必须“移除”?
答案 0 :(得分:7)
解析(我声称)有点像排序:这个问题在CS的早期就是很多思想的焦点,导致了一组理解得很好的解决方案,并带来了一些很好的理论结果。
我的主张是,我们在编译器课上得到(或者给予我们这些教授)的图片在某种程度上是对错误问题的美妙回答。
为了更直接地回答您的问题,LL(1)语法无法解析您可能要解析的各种事物;例如,“if”的“自然”表述,带有可选的“else”。
但是等等!我不能将我的语法重新表达为LL(1)语法,然后通过在其上行走来修补源树吗?你当然可以!在某种程度上,这就是你的解析器使用什么样的语法的问题。
另外,当我还是一名本科生(1990-94)时,空格敏感的语法显然是魔鬼的作品;现在,Python和Haskell的设计将空白灵敏度带回了光明之中。此外,Packrat解析说“要理解你的理论纯度:我只是将解析器定义为一组规则,我不关心我的语法属于哪个类。” (转述)
总之,我同意我认为你暗示的建议:在2009年,明确理解LL(k)和LR(k)之间的差异本身并不比制定能力更重要并调试使您的解析器生成器满意的语法。
答案 1 :(得分:1)
LL和LR之间的区别主要在于先行机制。人们普遍认为LR解析器带有更多的“上下文”。要实际看到这一点,请考虑以S作为起始符号的递归语法定义:
A -> Ax | x
B -> Ay
C -> Az
S -> B | C
当k是一个小的固定值时,解析像xxxxxxy这样的字符串是一个更适合LR解析器的任务。然而,如今流行的LL解析器(如ANTLR)不会将k限制为如此小的值,并且大多数人不再关心。
我希望这或多或少与你的问题一致。当然,Knuth表明任何明确的无上下文语言都可以被某些LR(1)语法识别。但是,在实践中我们也关注翻译。
作为旁注:您可能也喜欢阅读http://www.antlr.org/article/needlook.html。
这绝不是证明,但我一直质疑类似LR的解析是否与读取某些符号时大脑的工作方式非常相似。例如,在阅读英语句子时,很明显我们从左到右阅读。但是,请考虑以下模式:
。 。 。 。 。 | 。 。 。 。
我宁愿期待像这样的短图案人们从左到右依次不读“点点点点圆点圆点圆点”,而是平行处理图案或者至少在某种程度上处理图案模糊迭代方式。换句话说,我不相信我们必须使用LL / LR解析器采用的线性超前类型以从左到右的方式读取所有模式。
此外,如果我们可以使用LR(1)语法描述任何无上下文的语言,那么很明显,简单地识别字符串与“理解”字符串不同。
答案 2 :(得分:0)
嗯,首先,LL(k)语法中的左递归定义是不可能的(据我所知),不了解其他语法。这样就不可能将其他事情定义为大量的痛苦。例如,将表达式放在一起可以很容易地用左递归语言(伪代码):
lexer rule expression = other rules
| expression
| '(' expression ')';
至于可以通过左递归进行的语法上有用的东西,嗯,更简单的语法算作语法上的有用吗?
答案 3 :(得分:-1)
语言的功能不受其语法和语法的限制。
可以使用LL(k)语法定义任何语言特征,它可能对人类来说可能不太可读。