自上而下的语法分析

自上而下的语法分析

自上而下 指 从文法的开始符号开始,逐步向下构建出整棵语法树。这样的分析过程会遇到以下问题:

  • 文法左递归:导致分析过程无限循环

  • 回溯问题:在进入错误分支后,需要恢复进入分支前的现场,导致程序设计复杂

构造不会陷入死循环的、不需要回溯的语法分析算法

1. 消除左递归

文法可以消除左递归的条件

  • 不含以 \(\epsilon\) 为右部的产生式

  • 不含回路,即不存在 \(P⟹ P\)

1.1 直接左递归

\(P\rightarrow P\alpha|\beta\)

该产生式表示 以一个beta开头,以若干个alpha结尾的串 。即 \(\beta\alpha^\*\)

需要设计出不含左递归的文法:左递归变右递归。

\(P\rightarrow \beta P'\)

\(P'\rightarrow \alpha P'|\epsilon\)

容易知道,这两种文法是等价的。

CAUTION 左递归的实质是语法树不断增长而输入串的匹配没有任何推进,左递归改为右递归后,虽然语法树还可能一直增长,但是输入串的匹配指针在一直前进。

推广 更复杂产生式的直接左递归消除

含有左递归的文法 \(P\rightarrow P\alpha_1|P\alpha_2|...|P\alpha_m|\beta_1|\beta_2|...|\beta_n\)

如此改为右递归:

\(P\rightarrow \beta_1P'|\beta_2P'|...|\beta_nP'\)

\(P'\rightarrow \alpha_1P'|\alpha_2P'|...|\alpha_mP'|\epsilon\)

1.2 间接左递归

\(S\rightarrow Qc|c\)

\(Q\rightarrow Rb|b\)

\(R\rightarrow Sa|a\)

若从 \(S\) 开始推导,会出现 \(S\rightarrow Qc\rightarrow Rbc\rightarrow Sabc\) ,出现循环,为间接左递归。

为了消除这种依赖关系,进行如下操作:

\(Q\rightarrow Rb|b \rightarrow Sab|ab|b\)

\(S\rightarrow Sabc|abc|bc|c\)

这样出现了直接左递归,可用之前提到的方法进行消除,即:

\(S\rightarrow abcS'|bcS'|cS'\)

\(S'\rightarrow abcS'|\epsilon\)

由于文法的轮换对称性,对其他非终结符进行操作得到的文法是等价的。

1.3 消除左递归的通用算法

sort(G) # 把文法G的所有非终结符按照一种顺序排列 P_1, P_2, ..., P_N
for i in range(1, N + 1): # 把P_i的规则修改为 P_i -> a...|P_{i+1}...|P_{i+2}...|...
                          # 即只能包含P_i之后的非终结符

    for j in range(1, i):
        把形如 P_i -> P_j\gamma 的产生式中的P_j替换为它的定义式(消除P_j)
    消除关于P_i的直接左递归
simplify(G) # 化简文法G,删除无法到达的文法

由于在外层循环执行到 \(i\) 时, \(P_j\) 均已经只包含 \(P_j\) 之前的非终结符,所以该算法可以正确执行,消除 \(P_i\) 的左递归。

2. 消除回溯

为了消除回溯,必须保证选派的候选项可以 \(100\%\) 成功匹配输入串,这就要求我们合理构造 \(First\) 集合和 \(Follow\) 集合。

2.1 First集合

假设当前需要匹配的输入串的 token 为 \(a\) ,那么文法的规则中只能有至多一个以 \(a\) 开头,或者可以推导出以 \(a\) 开头的串,由此定义候选项的 \(First\) 集合:

\(First(\alpha)=\{\alpha ⟹ a..., a\in T \}\)

特别地,若 \(alpha\) 可以推导出 \(\epsilon\) ,则 \(\epsilon\in First(\alpha)\)

在满足 文法的规则中只能有至多一个以 \(a\) 开头 这一条件的情况下,若 \(a\in First(\alpha_i)\) ,则一定会选派 \(\alpha_i\) 去完成对 \(a\) 的匹配。

但是并非所有的文法都满足上述条件,即 \(First(\alpha_i)\cap First(\alpha_j)=\emptyset\) ,有时可以通过提取左公共因子的方式使其满足这一条件。

2.2 Follow集合

上述的 \(First\) 集合没有考虑使用 \(\epsilon\) 进行匹配的情况。

\(a\) 可以被 \(\epsilon\) 匹配,则 \(a\) 一定可以紧跟在当前正在匹配的非终结符 \(A\) 的后面出现,即存在一条规则,满足 \(P\rightarrow ...Aa...\) 。由此定义非终结符的 \(Follow\) 集合:

\(Follow(A)=\{S ⟹ ...Aa...,a\in T\}\)

特别地,若 \(S\) 可推导出 \(...A\) ,则 \(\epsilon\in Follow(A)\)

2.3 LL(1)文法

  • 文法不含左递归

  • 文法中的每个非终结符的产生式的所有候选项的 \(First\) 集合两两不相交

  • 对于文法中的每个非终结符,若它的某个候选项的 \(First\) 集合中存在 \(\epsilon\) ,则所有候选项的 \(First\) 集合不能和该非终结符的 \(Follow\) 集合相交

上述第三条规则保证了不会出现 \(a\) 可以同时匹配某个非空候选项和 \(\epsilon\) 的情况。

LL(1)文法分析过程

  1. \(a\in First(\alpha_i)\) ,则指派 \(\alpha_i\) 完成匹配

  2. \(a\) 不属于任何一个候选项的 \(First\) 集合,则:

若某个候选项的 \(First\) 集合中有 \(\epsilon\) ,且 \(a\in Follow(A)\) ,则用 \(\epsilon\) 匹配。

否则, \(a\) 的出现为语法错误。

2.4 First集合的构造

对于每个 \(X\in V_T\cup V_N\) ,连续使用下面的规则,直到每个候选项的 \(First\) 集合均不再发生变化:

  1. \(X\in V_T\) ,则 \(First(X)=X\)

  2. \(X\in V_N\) ,且有产生式 \(X\rightarrow a\) ,则将 \(a\) 加入 \(First(X)\) 中。特别地,若 \(X \rightarrow \epsilon\) 也是一条产生式,则也将 \(\epsilon\) 加入 \(First(X)\) 中。

  3. \(X\rightarrow Y...\) 是一条产生式,且 \(Y\in V_N\) ,则将 \(First(Y)\) 中所有非 \(\epsilon\) 元素加入 \(First(X)\) 中。

  4. \(X\rightarrow Y_1Y_2...Y_{i-1}Y_i...Y_k\) 是一个产生式,\(Y_1, Y_2, ..., Y_{i-1}\in V_N\) ,且 \(Y_1\)\(Y_{i-1}\)\(First\) 集合中均含有 \(\epsilon\) ,则将 \(First(Y_i)\) 中的非 \(\epsilon\) 元素加入 \(First(X)\) 。特别地,若 \(Y_1\)\(Y_k\)\(First\) 集合中均含有 \(\epsilon\) ,则将 \(\epsilon\) 加入 \(First(X)\)

虽然一个产生式 \(X\rightarrow Y_1Y_2...Y_k\) 的前几个非终结符可能含有 \(\epsilon\) ,但是这些 \(\epsilon\) 的后面还可能会有别的串,所以在第 \(4\) 步中,只有所有非终结符均含有 \(\epsilon\) 时,才将 \(\epsilon\) 加入 \(First(X)\)

构造任意符号串的First集合

对文法 \(G\) 的任何符号串 \(\alpha=X_1X_2...X_n\) 构造 \(First(\alpha)\) 的算法如下:

  1. \(First(\alpha) = First(X_1)\)

  2. 若对于任意 \(1\leq j\leq i-1\) ,均有 \(\epsilon\in First(X_j)\) ,则置 \(First(\alpha)=First(\alpha)\cup (First(X_i)\) \ \(\{\epsilon\})\) 。特别地,若 \(X_1\)\(X_n\)\(First\) 集合均含有 \(\epsilon\) ,则置 \(First(\alpha)=First(\alpha)\cup\{\epsilon\}\)

2.5 Follow 集合的构造

假设空串用 # 表示,对于文法 \(G\) 的每个非终结符 \(A\) 构造 \(Follow\) 集合的办法是,连续使用下面的规则,直到所有非终结符的 \(Follow\) 集合都不再发生变化:

  1. 对于文法的开始符号 \(S\) ,将 # 加入 \(Follow(S)\)

  2. \(A\rightarrow \alpha B\beta\) 是一个产生式,则将 \(First(\beta)\) \ \(\{\epsilon\}\) 中的元素加入 \(Follow(B)\)

  3. \(A\rightarrow \alpha B\) 是一个产生式,则将 \(Follow(A)\) 中的元素加入 \(Follow(B)\) 中。

  4. \(A\rightarrow \alpha B\beta\) 是一个产生式,且 \(\beta ⟹ \epsilon\) ,则将 \(Follow(A)\) 中的元素加入 \(Follow(B)\) 中。

3 递归下降分析器

按照上述方法进行分析即可,程序实现较为简单。

posted @ 2023-03-22 23:20  Franky0705  阅读(85)  评论(0编辑  收藏  举报