自顶向下解析器与LL(1)语法
约 1463 字大约 5 分钟
2025-01-09
解析器主要分为自顶向下和自底向上两种。这一节主要介绍自顶向下解析器和LL(1)语法。
自顶向下解析器
- 是一种自顶向下的解析器,它从开始符号开始,逐步将输入串分解为更小的子串,直到整个输入串被解析为一个语法树。自顶向下解析器通常使用递归下降法来实现。
- 分析树的构造方法,从根部开始
自底向上解析器
- 是一种自底向上的解析器,它从输入串的底部开始,逐步将输入串分解为更大的子串,直到整个输入串被解析为一个语法树。自底向上解析器通常使用自底向上的语法分析方法来实现。
- 分析树的构造方法,从叶子开始。
递归下降的语法分析
这一节主要介绍递归下降的语法分析方法。 递归下降的语法分析是一种自顶向下的语法分析方法,它使用递归函数来解析输入串。递归下降的语法分析方法通常使用递归函数来实现。
数据结构上需要用到一个输出缓冲区和一个预测的指针(lookahead) 分析过程
- 自左向右扫描输入串
- 设计一个辅助过程match(,将lookahead指向的位置与产生式迭代生
- 成的终结符进行匹配,如匹配,将lookahead挪到下一个位置
- 为每一个非终结符写一个分析过程
- 该过程可以调用其他非终结符的过程及match
- 这些过程可能是递归的。
给出伪代码:
function parseExpression() {
parseTerm();
while (lookahead in ['+', '-']) {
match(lookahead);
parseTerm();
}
}
function parseTerm() {
parseFactor();
while (lookahead in ['*', '/']) {
match(lookahead);
parseFactor();
}
}
function parseFactor() {
if (lookahead == '(') {
match('(');
parseExpression();
match(')');
} else if (isNumber(lookahead)) {
matchNumber();
} else {
error();
}
}
消除直接左递归
考虑一个简单文法S→Sa∣b,我们使用递归下降的语法分析方法来解析该文法。我们注意到,该文法是左递归
的,自顶向下分析方法无法处理左递归。
可能的推导过程如下:
- S→Sa (应用第一个产生式)
- →Saa (再次应用第一个产生式)
- →Saaa (继续应用第一个产生式)
- →... (无限循环)
这种左递归会导致递归下降解析器陷入无限递归,因为每次尝试解析S时都会再次调用解析S的过程。要解决这个问题,我们需要消除左递归,原字符串的特定是 baaaa......。我们可以修改成下面的文法,就可以正常工作了:
S→bS′
S′→aS′∣ϵ
这样修改后,解析器就可以正常工作了。
下面我们给出消除直接左递归的一般方法
对于单个左递归产生式:
A→Aα∣β
我们可以将其改写为:
A→βA′
A′→αA′∣ϵ
对于多个左递归产生式的情况:
A→Aα1∣Aα2∣...∣Aαn∣β1∣β2∣...∣βm
改写后的文法为:
A→β1A′∣β2A′∣...∣βmA′
A′→α1A′∣α2A′∣...∣αnA′∣ϵ
示例: 给定文法: A→Aa∣Ab∣c∣d
- 识别左递归产生式:A→Aa 和 A→Ab
- 提取非左递归产生式:c 和 d
- 引入新非终结符A′
- 改写文法: A→cA′∣dA′
A′→aA′∣bA′∣ϵ
改写后的文法消除了直接左递归,同时保持了原语言的表达能力。
上面是消除直接左递归的例子,下面给出如何消除间接左递归,间接左递归相比直接左递归不是十分明显,但消除起来也不是很困难。
消除间接左递归
间接左递归是指通过多个产生式间接形成的左递归。例如:
A→Ba
B→Ab∣c
消除间接左递归的步骤如下:
- 对非终结符进行排序:A1,A2,...,An
- 对于每个Ai,从A1到Ai−1,将Aj→Aiα替换为Aj→β1α∣β2α∣...∣βkα,其中Ai→β1∣β2∣...∣βk是Ai的所有产生式
- 消除Ai的直接左递归
示例: 给定文法: A→Ba∣a
B→Ab∣c
- 排序非终结符:A,B
- 处理 A:没有间接左递归
- 处理 B:
- 将 B→Ab替换为B→Baa∣aa(因为 A→Ba∣a)
- 现在 B有直接左递归:B→Baa∣aa∣c
- 消除 B 的直接左递归: B→aaB′∣cB′
B′→aaB′∣ϵ
最终消除间接左递归后的文法:
A→Ba∣a
B→aaB′∣cB′
B′→aaB′∣ϵ
左公共因子
左公共因子是指多个产生式具有相同的前缀。例如:
A→aB∣aC∣d
其中a是左公共因子。左公共因子会导致预测分析时出现不确定性,因为无法确定选择哪个产生式。
提取左公共因子的步骤如下:
- 找出具有相同前缀的产生式
- 提取公共前缀,引入新的非终结符
- 将剩余部分作为新产生式的右部
示例: 给定文法: A→aB∣aC∣d
- 找出左公共因子:a出现在前两个产生式中
- 提取公共前缀,引入新非终结符A′
- 改写文法: A→aA′∣d, A′→B∣C
改写后的文法消除了左公共因子,使得预测分析更加明确。
左公共因子提取的通用形式: 对于产生式: A→αβ1∣αβ2∣...∣αβn∣γ1∣...∣γm
提取左公共因子α后: A→αA′∣γ1∣...∣γm, A′→β1∣β2∣...∣βn
更新日志
578c3
-更新自底向上和LR(1)语法于