在学习自下而上解析(例如 LALR)时,您需要做的第一件事是记住它与自上而下解析完全不同。自上而下的解析从非终结符开始,即产生式的左侧 (LHS),并猜测使用哪个右侧 (RHS)。另一方面,自下而上的解析从识别 RHS 开始,然后计算出要选择的 LHS。
更具体地说,自下而上的解析器将传入的令牌累积到队列中,直到右侧位于队列的右侧。然后它通过用相应的 LHS 替换它来减少RHS,并检查适当的 RHS 是否位于修改后的累加输入的右侧边缘。它继续这样做,直到它决定在输入中的该点不再发生减少,然后读取一个新令牌(或者,换句话说,获取下一个输入令牌并将其转移到队列的末尾。 )
这一直持续到读取最后一个标记并执行所有可能的缩减,此时如果剩下的是作为“开始符号”的单个非终结符,则它接受解析。
解析器不必仅仅因为它出现在当前队列的末尾就减少 RHS,但它不能减少不在队列末尾的 RHS。这意味着它必须在转移任何其他代币之前决定是否减少。由于该决定并不总是显而易见的,它可能会检查一个或多个它尚未读取的标记(“前瞻标记”,因为它正在查看输入)以便做出决定。但它只能查看下一个k
令牌的某个值k
,通常为 1。
这是一个非常简单的例子;逗号分隔列表:
1. Start -> List
2. List -> ELEMENT
3. List -> List ',' ELEMENT
假设输入是:
ELEMENT , ELEMENT , ELEMENT
一开始,输入队列是空的,由于没有 RHS 是空的,唯一的选择是移位:
queue remaining input action
---------------------- --------------------------- -----
ELEMENT , ELEMENT , ELEMENT SHIFT
下一步,解析器决定使用产生式 2 进行归约:
ELEMENT , ELEMENT , ELEMENT REDUCE 2
现在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
现在,前瞻标记是“输入结束”伪标记。这一次,它确实决定减少:
List -- REDUCE 1
Start -- ACCEPT
并且解析成功。
这仍然留下了一些问题。首先,我们如何使用 FIRST 和 FOLLOW 集?
作为一个简单的答案,如果不知道可能跟随该非终结符的非终结符的 FIRST 集,就无法计算非终结符的 FOLLOW 集。我们可以决定是否应该执行归约的一种方法是查看前瞻是否在归约目标非终结符的 FOLLOW 集中;如果不是,则肯定不应该执行减少。该算法对于上面的简单文法来说已经足够了,例如: 的减少Start -> List
是不可能的,,因为,is not in FOLLOW(Start)
。可以以这种方式解决唯一冲突的语法是SLR
语法(其中S
代表“简单”,它肯定是)。
对于大多数语法来说,这还不够,还需要进行更多的分析。一个符号可能在非终结符的 FOLLOW 集中,但不在导致当前堆栈配置的上下文中。为了确定这一点,我们需要更多地了解我们是如何获得当前配置的;各种可能的分析导致LALR
和IELR
规范LR
解析,以及其他可能性。