左/右递归,左/右派生,优先,相关性等之间的差异

时间:2015-08-20 16:00:10

标签: parsing compiler-construction grammar

我目前正在学习语言处理器,而且经常出现的主题是语法中的元素被消耗的方向。从左到右或从右到左。 我理解这个概念,但似乎有很多方法来编写这些规则,我不确定它们是否完全相同。到目前为止我所看到的是:

右/左递归, 右/左最推导, 右/左减少,优先,相关等。

这些都意味着同样的事情吗?

2 个答案:

答案 0 :(得分:9)

不,它们都有不同的含义。

右递增和左递归是指生产规则中的递归。如果非终端的生产可以导出包含该非终端的序列,则该生产是递归的。如果非终端可以出现在派生序列的开始(左边缘),则它是左递归的,如果它可以出现在末尾(右边缘),则是右递归。生产可以递归,而不是左递归或右递归,甚至可以是左递归和右递归。

例如:

term: term '*' factor            { /* left-recursive */ }
assignment: lval '=' assignment  { /* right-recursive */ }

以上例子都是直接递归;非终端直接导出包含非终端的序列。递归也可以是间接的;它仍然是递归。

所有常见的解析算法从左到右处理,这是LL和LR中的第一个L.自上而下(LL)解析找到最左边的推导(第二个L),而自下而上(LR)解析找到最右边的推导(R)。

实际上,两种类型的解析器都以单个非终端(起始符号)和"猜测"基于当前序列中的一些非终端的派生,直到导出输入文本。在最左边的派生中,它总是最左边的非终端被扩展。在最右边的派生中,它始终是最右边的非终端。

因此,自上而下的解析器总是猜测哪个生产用于第一个非终端,之后它需要再次处理现在的第一个非终端。 ("猜猜"这里是非正式的。它可以查看要匹配的输入 - 或至少输入的下一个 k 标记 - 以确定要生成的输入使用。)这称为自上而下处理,因为它从上到下构建解析树。

更容易(至少对我来说)反向自由地显示自下而上解析器的动作;它通过反复读取足够的输入来构建解析树,以找到一些生成,这将是派生链中的最后一个派生。所以它确实产生了最右边的推导,但它会从前到后输出它。

在运算符语言的LR语法中(粗略地说,语言类似于算术表达式的语法),左右相关性分别使用左递归语法规则和右递归语法规则建模。 "结合性"是语法的非正式描述,"优先"。

优先级是通过使用一系列语法规则建模的,每个规则都引用下一个规则(并且通常最终会产生用于处理括号的递归生成 - '(' expr ')' - 这既不是左边也不是右递归)。

有一种较旧的自下而上解析方式,称为"运算符优先解析",其中优先级明确是语言描述的一部分。一种常见的运算符优先算法是所谓的Shunting Yard算法。但是如果你有一个LALR(1)解析器生成器,比如bison,你也可以使用它,因为它更通用,更精确。

答案 1 :(得分:1)

(我不是解析器和编译器理论方面的专家。我碰巧学习了一些相关的东西。而且我想分享我到目前为止所发现的东西。)

我强烈建议您查看此awesome article

它解释了插图 LL和LR算法。你可以清楚地看到为什么LL被称为自上而下,LR被称为自下而上。

一些引用:

  

LL和LR解析器如何运作的主要区别在于   LL解析器输出解析树和LR的预订序遍历   解析器输出一个后序遍历。

     

...

     

我们正在聚合一个非常简单的LL和LR解析器模型   操作。两者都读取输入令牌流并输出相同的令牌   流,在适当的地方插入规则来实现   解析树的预订(LL)或后订购(LR)遍历。

     

...

     

当您看到LL(1),LR(0)等名称中的数字时   括号是前瞻标记的数量。

至于首字母缩略词:(source

  

LR LL 中的第一个 L 表示:解析器读取输入    一个方向上的文字,无需备份 ;那个方向通常是   在每条线内从左到右,从上到下穿过线   完整的输入文件。

剩余的 R L 分别表示:最右侧和最左侧的派生。< / p>

这是两种不同的解析策略。解析策略确定要重写的下一个非终端。 (source

  • 对于最左边的派生,它始终是最左边的非终结。
  • 对于最右边的推导,它总是最右边的非终结。