编译原理-语法分析-自上而下分析

语法分析器的功能

语法分析器是编译过程的核心部分。任务是在词法分析识别出的单词符号串的基础上,分析并判定程序的语法结构是否符合语法规则。
image

自上而下分析面临的问题

  1. 左递归:\({p}->{p}{\alpha}|{\beta}\):会使程序陷入死循环,导致不可实现
  2. 回溯:试探法就是穷举所有可能,一旦遇到不匹配就进行回溯,尝试下一种可能,这种方法只在理论上有意义,由于回溯穷举时间开销巨大所以不太具有实践意义。

LL(1)分析法

左递归的消除

直接左递归

直接左递归的形式:$${p}->{p\alpha}|{\beta}$$ \({\beta}\)不以\({p}\)开头
这个推导:

\[{p => p\alpha => p\alpha\alpha => ... => \beta\alpha....\alpha} \]

从这个推到我们可以看出\({p}\)推出的串是以\({\beta}\)后面跟着若干个\({\alpha}\)组成的串
这样我没对这个串做等价变换:

\[{p->\beta p'} \]

\[{p'->\alpha p'|\epsilon} \]

我们可以验证这个文法是不是和上面左递归形式的文法等价,我们对这个文法做推导看看会推导出哪些串

\[{p => \beta p' => \beta \alpha p' => ... => \beta \alpha ...\alpha} \]

我们得到的串同样是以\({\beta}\)开头后面接跟着若干个\({\alpha}\)的串
我们对上面的结论进行推广:
对于P的全部产生式是

\[{P -> P\alpha_1|P\alpha_2|...|P\alpha_m|\beta_1|\beta_2|...|\beta_n},其中每个{\alpha}都不以{\epsilon}开头,每个\beta都不以P开头 \]

我们直接进行右递归改造:

\[{P -> \beta_1P’|\beta_2P’\|...|beta_nP’} \]

\[{P’->\alpha_1P’|\alpha_2P’|...|\alpha_mP'} \]

例子:
\({E->E+T|T}\)
\({T->T*F|F}\)
\({F->(E)|i}\)
对于第一个式子有直接左递归我们进行消除,首先第一个式子最终的推导串是以\({T}\)开头后面若干个{+T}我们进行等价变换:$${E->TE'}$$ $${E'->+TE'|\epsilon}$$
对于第二个式子也是直接左递归的我们也需要进行消除,首先第二个式子最终推导出的串的形式下以\({F}\)开头后面若干个*F,我们根据这个进行等价变换
\({T->FT'}\)
\({T'*FT'|\epsilon}\)
最后一个式子没有直接左递归不用进行处理

非直接左递归

\[文法G(S) \]

\[{S -> Qc|c} \]

\[{Q -> Rb|b} \]

\[{R -> Sa|a} \]

形式上这个文法并没有直接左递归,但是实际上这个文法是左递归的,进行如下推导就可以看出

\[{S => Qc => Rbc => Sabc} \]

经过推导我们发现S实际上是左递归的
我们从上面的文法可以看出S是由Q定义的,Q是由R定义的,R是由S定义的,那么我们可以得到下图
image
\({S -> Qc|c}\)
\({Q -> Rb|b}\)
\({R -> Sa|a}\)
我们将Q中的R替换掉
image
\({S -> Qc|c}\)
\({Q -> Sab|ab|b}\)
\({R -> Sa|a}\)
再将S中的Q替换掉
image
\({S ->Sabc|abc|bc|c}\)
\({Q -> Sab|ab|b}\)
\({R -> Sa|a}\)

消除左递归的算法

1.把文法G的所有非终结符按任意一种顺序排列成\({P_1 P_2, ... ,P_n}\)按此顺序执行
2.
\(FOR\quad i:=1\quad TO\quad n\quad DO\)
\(BEGIN\)
\(\quad \quad FOR \quad j:=1 \quad TO \quad i-1\quad DO\)
$ \quad \quad 把形如P_i->P_j \gamma 的规则改写成,P_i-> \delta_1 \gamma|\delta_2 \gamma|...|\delta_k \gamma (其中P_j -> \delta_1|\delta_2|...|\delta_k是关于P_j的所有规则) $

消除回溯、提左因子

FIRST

为了消除回溯,我们首先需要引入两个概念一个是FIRST集一个是FOLLW集
FIRST:
零G是一个不含左递归的文法,对G的所有非终结符的每一个候选\(\alpha\)定义它的终结首符集\(FIRST(\alpha)\)
image
那么当要求A匹配输入串时,A就能根据它所面临的第一个输入符号a,准确地指派某一个候选前去。这个候选就是那个首符集含\(a\)\(\alpha\)

提左因子

很多文法都存在不满足所有候选式的终结首符集不两两相交的。我们采用提取公共左因子的办法
假设关于A的规则是:

\[{A\rightarrow \delta \beta_1 | \delta \beta_2 | ... |\delta \beta_n| \gamma_1 | \gamma_2| ... | \gamma_m} \quad ,(其中,每个 \gamma 不以 \delta 开头) \]

那么可以把这些规则改写成

\[{A \rightarrow \delta A' | \gamma_1 | \gamma_2| ... | \gamma_m} \]

\[{A' \rightarrow \beta_1 | \beta_2 | ... | \beta_n} \]

经过上面反复提取左因子,我们一定可以将一个非终结符的所有候选式的FIRST集两两不相交

FOLLOW集

消除左递归后的文法

\[{E \rightarrow TE'} \]

\[{E' \rightarrow +TE'|\epsilon} \]

\[{T \rightarrow FT'} \]

\[{T' \rightarrow * FT'|\epsilon} \]

\[{F \rightarrow (E)|i} \]

我们对$ i + i $进行至上而下的语法分析

image
当我们进行到这个地方的时候我们发现\(T'有两个候选式{T' \rightarrow * FT'|\epsilon},但是 * 不能和 + 匹配,另一个候选式是{\epsilon} ,这时我们不能选择 * FT' 去扩展,只能选择 {\epsilon}\)
可以看到当候选式有\({\epsilon}\)时,会对我们的分析产生困难,如何消除这个困难呢?什么时候才能用\({\epsilon}\)去扩展呢?
这里我们就引入了\(FOLLOW集\)
image

LL(1)的分析条件

LL(1)文法

image

  1. 文法不含左递归
  2. 对文法中每一个非终结符A的各个产生式的候选终结首符集两两不相交,

\[A \rightarrow \alpha_1 | \alpha_2 | ... | \alpha_n \]

\[则 FIRST( \alpha_i ) \cap FIRST( \alpha_j ) = \varnothing \]

  1. 对文法中的每个非终结符A,若它存在某个候选首符集包含 $ { \epsilon }$

\[{ FIRST(A) \cap FOLLWO(A)} \]

对于一个LL(1)文法,我们可以对输入串进行有效的无回溯的至上而下分析,假设我们要用非终结符\(A做匹配,面临的输入符号为 \alpha, A\)的所有产生式为:

\[A \rightarrow \alpha_1 | \alpha_2 | ... | \alpha_n \]

  1. \(a \in FIRST(\alpha_i)则指派 \alpha_i\)匹配

  2. 若$ a $不属于任何一个候选首符集,则:

    1. \(\epsilon \in FIRST(\alpha_i)并且a \in FOLLOW(A)则让A与 \epsilon自动匹配\)
    2. 否则a的出现是一种语法错误

构造FIRST和FOLLOW集合

构造每个文法符号的FIRST集合

对每一文法符号 $ X \in V_T \cup V_N构造FIRST(X) $
连续使用下面几条规则,直到每个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)中 $
    image

构造FOLLOW集合

再回顾一遍FOLLOW集合的定义:

\[FOLLOW(A) = \{ a| S \Rightarrow ...Aa...,a \in V_T\} \]

对于文法G的每个非终结符A构造FOLLOW(A),连续使用下面的规则,直至每个FOLLOW不再增大为止

  1. 对于文法的开始符号 \(S ,置 \# 于FOLLOW(S)中\)
  2. \(A \rightarrow \alpha B \beta 是一个产生式,则把FIRST({ \beta }) | \epsilon 加至 FOLLOW(B)中\)
  3. \(A \rightarrow \alpha B 是一个产生式,或 A \rightarrow \alpha B \beta是一个产生式,但 \beta \Rightarrow \epsilon\)则把 \(FOLLOW(A)加入到FOLLOW(B)\)

递归下降分析程序

递归下降分析程序

image
image
image

\[文法G(E): \]

\[{E \rightarrow TE'} \]

\[{E' \rightarrow +TE'|\epsilon} \]

\[{T \rightarrow FT'} \]

\[{T' \rightarrow * FT'|\epsilon} \]

\[{F \rightarrow (E)|i} \]

image
image

文法的另一种表示方法和转换图

image
image

文法
\(E \rightarrow T | E + T\)
\(T \rightarrow F | T * F\)
\(F \rightarrow i | (E)\)
用巴克斯范式可以表示为:
\(E \rightarrow T \{ +T \}\)
\(T \rightarrow F \{ * F \}\)
\(F \rightarrow i | (E)\)

我们还可以用语法图来表示,这种图的每一个子图是一个非终结符,子图和子图之间可以相互引用
image
根据语法图和上面的巴克斯范式我们可以写出递归下降程序
image

预测分析程序

预测分析程序的工作过程

image
image
预测分析的总控程序:
image

预测分析表的构造

image
image

posted @ 2023-10-23 13:58  cxy8  阅读(193)  评论(0编辑  收藏  举报