LALR 解析器和前瞻

Ton*_*nis 0 parsing lalr

我正在无缘无故地实现 LALR 解析表的自动构建。此解析器有两种风格,LALR(0) 和 LALR(1),其中数字表示前瞻量。

我让自己对前瞻的含义感到困惑。

如果我的输入流是 'abc' 并且我有以下产品,我需要 0 前瞻还是 1?

    P :== a E

同样的问题,但我无法通过仅查看输入中的“a”来提前选择正确的 P 产生式。

    P :== ab E
      | 抗体

我有额外的困惑,因为我认为在构建 LALR 解析器生成器时不会真正发生后面的 P 生成。原因是当我们计算闭包时,语法会自动有效地进行左因子分解。

正在浏览这个页面,直到我到达第一个/跟随部分为止。我的问题是我不知道我们为什么要计算这些东西,所以我无法在脑海中抽象出来。

我几乎明白前瞻与转移输入无关,而是决定何时减少

我一直在读龙的书,但它和塔伦蒂诺的剧本一样线性。对于已经知道如何做到这一点的人来说,这似乎是一个很好的参考。

ric*_*ici 5

学习自底向上解析(如 LALR)时,首先需要记住它与自顶向下解析完全不同。自上而下的解析从一个非终结符开始,即产生式的左侧 (LHS),并猜测要使用哪个右侧 (RHS)。另一方面,自下而上的解析从识别 RHS 开始,然后找出要选择的 LHS。

更具体地说,自底向上的解析器将传入的令牌累积到队列中,直到右侧位于队列的右侧。然后它通过用相应的 LHS 替换它来减少该 RHS,并检查是否合适的 RHS 位于修改后的累积输入的右侧边缘。它不断这样做,直到确定没有更多的削减将发生在输入这一点上,然后读取新的令牌(或者,换句话说,需要令牌下一个输入和转移它到队列的末尾。 )

这一直持续到读取最后一个标记并执行所有可能的归约,此时如果剩下的是作为“开始符号”的单个非终结符,则它接受解析。

解析器不必仅仅因为 RHS 出现在当前队列的末尾就减少它,但它不能减少不在队列末尾的 RHS。这意味着它必须在转移任何其他令牌之前决定是否减少。由于决定并不总是显而易见的,它可能会检查一个或多个尚未读取的标记(“先行标记”,因为它正在查看输入)以做出决定。但它只能查看下一个k标记的某个值k,通常为 1。

这是一个非常简单的例子;逗号分隔列表:

1. Start -> List
2. List  -> ELEMENT
3. List  -> List ',' ELEMENT
Run Code Online (Sandbox Code Playgroud)

让我们假设输入是:

ELEMENT , ELEMENT , ELEMENT
Run Code Online (Sandbox Code Playgroud)

一开始,输入队列是空的,因为没有 RHS 是空的,唯一的选择是转移:

queue                   remaining input                 action
----------------------  ---------------------------     -----
                        ELEMENT , ELEMENT , ELEMENT     SHIFT
Run Code Online (Sandbox Code Playgroud)

在下一步,解析器决定使用产生式 2 减少:

ELEMENT                 , ELEMENT , ELEMENT             REDUCE 2
Run Code Online (Sandbox Code Playgroud)

现在List队列末尾有一个 a ,因此解析器可以减少使用产生式 1,但它决定不基于它,在传入输入中看到 a 的事实。这持续了一段时间:

List                    , ELEMENT , ELEMENT             SHIFT
List ,                  ELEMENT , ELEMENT               SHIFT
List , ELEMENT          , ELEMENT                       REDUCE 3
List                    , ELEMENT                       SHIFT
List ,                  ELEMENT                         SHIFT
List , ELEMENT          --                              REDUCE 3
Run Code Online (Sandbox Code Playgroud)

现在前瞻标记是“输入结束”伪标记。这一次,它确实决定减少:

List                    --                              REDUCE 1
Start                   --                              ACCEPT
Run Code Online (Sandbox Code Playgroud)

并且解析成功。

这仍然留下了一些问题。首先,我们如何使用 FIRST 和 FOLLOW 集?

作为一个简单的答案,如果不知道可能跟随该非终结符的非终结符的 FIRST 集,则无法计算非终结符的 FOLLOW 集。我们可以决定是否应该执行归约的一种方法是查看前瞻是否在归约的目标非终结符的 FOLLOW 集中;如果不是,则肯定不应该进行减少。该算法对于上面的简单语法就足够了,例如:Start -> List前瞻的 减少是不可能的,,因为,不在 中FOLLOW(Start)。唯一可以通过这种方式解决冲突的SLR语法是语法(其中S代表“简单”,它当然是)。

对于大多数语法来说,这还不够,还需要进行更多的分析。一个符号可能在非终结符的 FOLLOW 集合中,但不在导致当前堆栈配置的上下文中。为了确定这一点,我们需要更多地了解我们如何获得当前配置;各种可能的分析导致LALRIELR和规范的LR分析,除其他的可能性。