第七章-自上而下分析1
7.1 自上而下分析的基本问题
7.1.1语法分析的前提
对语言的语法结构进行描述
- 采用
正规式
和有限自动机
描述和识别语言的 单词符号 - 用
上下文无关文法
来描述语法规则
上下文无关文法即第二章的内容:
语法分析的任务
- 语法分析的任务 :分析一个文法的句子的结构
- 语法分析器的功能 :按照文法的产生式(语言的语法规则),识别输入符号 串是否为一个句子(合式程序)
7.1.2语法分析器在编译器中的地位
语法分析的过程:
7.1.3语法分析的方法
语法分析的方法有两种:自上而下
以及 自下而上
:
自下而上(Bottom-up) | 自上而下(Top-down) |
---|---|
从输入串开始,逐步进行归约,直到文法的开始符号 | 从文法的开始符号出发, 反复使用各种产生式,寻 找"匹配"的推导 |
归约:根据文法的产生式规则,把串中出现的产生 式的右部替换成左部符号 | 推导:根据文法的产生式 规则,把串中出现的产生 式的左部符号替换成右部 |
从树叶节点开始,构造语法树 | 从树的根开始,构造语法树 |
算符优先分析法、LR分析法 | 递归下降分析法、预测分析程序 |
7.1.4 自上而下分析面临的问题
自上而下分析的过程
基本思想:
- 从文法的开始符号出发,向下推导,推出句子
- 针对输入串,试图用一切可能的办法,从文法开始 符号(根结点)出发,自上而下地为输入串建立一棵语法树
一个例子:
从上面的例子可以看出:
多个产生式候选带来的问题
- 回溯问题:分析过程中,当一个非终结符用某一个候选匹配成 功时,这种匹配可能是暂时的,出错时,不得不“回溯”(一个dfs的过程)
- 文法左递归问题:可能某个终结符可以不断的使用自身来扩展下去,这样在语法树中就是一个不断左递归的过程,一个文法是含有左递归的,如果存在非终结符
P
:
P
→
P
a
P:P \to Pa
P:P→Pa ,这样的,会导致死循环:
7.2 LL(1)文法——消除文法的左递归
因为直接自上而下分析会有 回溯 和 左递归 问题,所以要先进行 消除文法的左递归性 以及 消除回溯 ,以 构造不带回溯的自上而下分析算法
7.2.1 直接左递归的消除
(看图吧,md排版不怎么好。。。)
例如这样的一个文法的消除例子:
7.2.2 间接左递归的消除
一个文法消除左递归的条件 :
- 不含以ε为右部的产生式
- 不含回路,就是不会出现一个非终结符推着推着出现了自己开头的
例如对这样一个文法S的间接左递归的消除:
这个过程就是不断的用下层的替换前面出现的,这样一直替换下去最后第一层就只有一个非终结符组成的文法了,最后有直接左递归的就用上一个方法去掉就行了。
这个间接左递归的具体消除算法如下:
对于上面的那个例子的消除结果就是:
{ S → a b c S ′ ∣ b c S ′ ∣ c S ′ S ′ → a b c S ′ ∣ ε \begin{cases} S → abcS'| bcS'| cS' \\ S'→ abcS'| ε \end{cases} {S→abcS′∣bcS′∣cS′S′→abcS′∣ε
- 注意,由于对非终结符排序的不同,后所得的文法在形式上可能不一样。但不难证明,它们都是等价的。
7.3 LL(1)文法——消除回溯
7.3.1 消除回溯的目的
为了消除回溯必须保证:对文法的任何非终结符,当要它去匹配输入串时, 能够根据它所面临的输入符号 准确地 指派它的一个候选去执行任务,并且此候选的工作结果应是确信无疑的(将多种可能的搜索变成单一的匹配)。
7.3.2 FIRST集合
令G是一个不含左递归的文法,对G的所有 非终结符 的每个候选α定义它的终结首符集FIRST(α) 为:
F I R S T ( α ) = { a ∣ α ⇒ ∗ a . . . , a ∈ V T } 特 别 的 , 若 α ⇒ ∗ ε , 则 规 定 ε ∈ F I R S T ( α ) FIRST(\alpha)=\{a|\alpha⇒^*a..., a \in V_T\} \\ 特别的,若 \alpha ⇒^* ε,则规定 ε \in FIRST(\alpha) FIRST(α)={a∣α⇒∗a...,a∈VT}特别的,若α⇒∗ε,则规定ε∈FIRST(α)
就是某个非终结符 α \alpha α 其所有的可能推导出的句子的首字符组成的集合。
如果非终结符A的所有候选首符集两两不相交, 即A的任何两个不同候选
α
i
α_i
αi 和
α
j
α_j
αj :
F
I
R
S
T
(
α
i
)
∩
F
I
R
S
T
(
α
j
)
=
φ
FIRST(α_i)∩FIRST(α_j)=φ
FIRST(αi)∩FIRST(αj)=φ , 当要求A匹配输入串时,A能根据它所面临的 第一个输入符号a,准确地指派某一个候选去执行任务。这个候选就是那个终结首符集含a的α。 (也就说,只要我们可以使得A下的产生式的First集合两两不相交,这样就可以唯一确定一个字符出现在那个First集合中,这样就可以从该产生式扩展下去,也就达到了消除回溯的目的:
而达到这一目的(两两不相交)的方法就是 提取公共左因子
7.3.3 提取公共左因子(通过大量引入非终结符的方法)
7.3.4 ε ε ε候选
这样的一个例子:
在进行分析的过程中,对 T‘的推导中使用了
ε
ε
ε ,这里选用的原因之一就是 T’包含这样的一个句型,所以才使用
ε
ε
ε 来跳过:
7.3.5 FOLLOW集合
假定S是文法G的开始符号,对于G的任何非终结符A,我们定义A的FOLLOW集合:
F O L L O W ( A ) = { a ∣ S ⇒ ∗ . . . A a . . . , a ∈ V T } 特 别 是 , 若 S ⇒ ∗ . . . A , 则 规 定 # ∈ F O L L O W ( A ) FOLLOW(A)=\{a|S ⇒^* ...Aa..., a \in V_T\} \\ 特别是,若 S ⇒^* ...A, 则规定 \# \in FOLLOW(A) FOLLOW(A)={a∣S⇒∗...Aa...,a∈VT}特别是,若S⇒∗...A,则规定#∈FOLLOW(A)
(也就是指FOLLOW(A) 为所有可能跟在非终结符A后的终结符的集合)
7.3.6 构造不带回溯的自上而下分析的文法条件
- 文法不含左递归
- 对于文法中每一个非终结符A的各个产生式的候选首符集两两不相交。即,若 A → α 1 ∣ α 2 ∣ … ∣ α n A→α_1|α_2|…|α_n A→α1∣α2∣…∣αn ,则 F I R S T ( α i ) ∩ F I R S T ( α j ) = φ ( i ≠ j ) FIRST(α_i)∩FIRST(α_j)=φ(i≠j) FIRST(αi)∩FIRST(αj)=φ(i=j) (保证了不回溯)
- 对文法中的每个非终结符A,若它存在某个候选首符集包含ε,则 F I R S T ( α i ) ∩ F O L L O W ( A ) = φ , i = 1 , 2 , . . . , n FIRST(α_i)∩FOLLOW(A)=φ,i=1,2,...,n FIRST(αi)∩FOLLOW(A)=φ,i=1,2,...,n (保证在选取ε时仅选择的是唯一一个产生式)
如果一个文法G满足以上条件,则称该文法G为LL(1)文法。 其中:
- 第一个L: 从左到右扫描输入串
- 第二个L: 最左推导
- 1: 每一步只需向前查看一个符号
7.3.6 LL(1)分析法
对于经过处理后的满足上述条件的LL(1)文法,可以对其输入串进行 有效的 无回溯的 自上而下分析:
- 假设要用非终结符A进行匹配,面临的 输入符号为a,A 的所有产生式为 A → α 1 ∣ α 2 ∣ … ∣ α n A→α_1 | α_2 | … | α_n A→α1∣α2∣…∣αn
- 1.若 a ∈ F I R S T ( α i ) a∈FIRST(α_i) a∈FIRST(αi) ,则指派 α i α_i αi 执行匹配任务;
- 2.若a不属于任何一个候选首符集,则:
- 若 ε ε ε 属于某个 F I R S T ( α i ) FIRST(α_ i) FIRST(αi) 且 a ∈ F O L L O W ( A ) a∈FOLLOW(A) a∈FOLLOW(A) ,则让 A与 ε ε ε 自动匹配。
- 否则,a的出现是一种语法错误
7.4 FIRST和FOLLOW集合的构造
7.4.1 构造 F I R S T ( α ) FIRST(α) FIRST(α)
F I R S T ( α ) = { a ∣ α ⇒ ∗ a . . . , a ∈ V T } FIRST(\alpha)=\{a|\alpha ⇒^* a..., a \in V_T\} FIRST(α)={a∣α⇒∗a...,a∈VT}
对于First集合的构造,可以由易到难进行考虑,首先考虑 α α α 为单个字符的情况,然后推广到任意长度的串:
{ α = X , X ∈ V T ∪ V N (单个文法符号) α = X 1 X 2 … X n , X i ∈ V T ∪ V N (任何符号串) \begin{cases} α= X,X∈V_T∪V_N & \text{(单个文法符号)}\\ α= X_1X_2…X_n,X_i∈V_T∪V_N & \text{(任何符号串)} \end{cases} {α=X,X∈VT∪VNα=X1X2…Xn,Xi∈VT∪VN(单个文法符号)(任何符号串)
构造每个文法符号的FIRST集合
(思想:将无穷推导转化为对优先产生式的反复扫描)
构造任何符号串的FIRST集合
这里的 F I R S T ( X i ) / { ε } FIRST(X_i) / \{ε\} FIRST(Xi)/{ε} 表示的是除去空字集合。
7.4.2 构造FOLLOW(A)
F O L L O W ( A ) = { a ∣ S ⇒ ∗ . . . A a . . . , a ∈ V T } FOLLOW(A)=\{a|S ⇒^* ...Aa..., a \in V_T\} FOLLOW(A)={a∣S⇒∗...Aa...,a∈VT}
构造每个非终结符的FOLLOW集合
对于第2点:
对于第3点:
7.4.3 练习
对于上面的文法G(E):
E → T E ′ E ′ → + T E ′ ∣ ε T → F T ′ T ′ → ∗ F T ′ ∣ ε F → ( E ) ∣ i \begin{aligned} &E→TE' \\ &E'→+TE'| ε \\ &T→FT' \\ &T'→*FT'| ε \\ &F→(E) | i \\ \end{aligned} E→TE′E′→+TE′∣εT→FT′T′→∗FT′∣εF→(E)∣i
构造每个非终结符的FIRST和FOLLOW集合 :
根据前面构造FIRST集合的步骤:
FIRST | 第1步 | 第2步 | 第3步 | 第4步 |
---|---|---|---|---|
F I R S T ( E ) = FIRST(E)= FIRST(E)= | { ( , i \{(,i {(,i | { ( , i } \{(,i\} {(,i} | ||
F I R S T ( E ′ ) = FIRST(E')= FIRST(E′)= | { + , ε \{+, ε {+,ε | { + , ε \{+, ε {+,ε | { + , ε \{+, ε {+,ε | { + , ε } \{+, ε\} {+,ε} |
F I R S T ( T ) = FIRST(T)= FIRST(T)= | { ( , i \{(, i {(,i | { ( , i \{(, i {(,i | { ( , i } \{(, i\} {(,i} | |
F I R S T ( T ′ ) = FIRST(T')= FIRST(T′)= | { ∗ , ε \{*, ε {∗,ε | { ∗ , ε \{*, ε {∗,ε | { ∗ , ε \{*, ε {∗,ε | { ∗ , ε } \{*, ε\} {∗,ε} |
F I R S T ( F ) = FIRST(F)= FIRST(F)= | { ( , i \{(, i {(,i | { ( , i \{(, i {(,i | { ( , i \{(, i {(,i | { ( , i } \{(, i\} {(,i} |
有了FIRST集合,就可以构造FOLLOW集合(注意一个产生式可能有不同的匹配规则匹配):
- 第一次处理时加入 # \# #
- 满足: A → α B β A\to \alpha B \beta A→αBβ ,将 F I R S T ( β ) / { ε } FIRST(\beta)/\{ε\} FIRST(β)/{ε} 加入到 F O L L O W ( B ) FOLLOW(B) FOLLOW(B)
- 满足: A → α B β A\to \alpha B \beta A→αBβ ,且 β ⇒ ∗ ε \beta ⇒^*ε β⇒∗ε 将 F O L L O W ( A ) FOLLOW(A) FOLLOW(A) 加入到 F O L L O W ( B ) FOLLOW(B) FOLLOW(B) 中
- 满足: A → α B A \to \alpha B A→αB , 将 F O L L O W ( A ) FOLLOW(A) FOLLOW(A) 加入到 F O L L O W ( B ) FOLLOW(B) FOLLOW(B) 中
- 对于 ε ε ε 不处理
FOLLOW | 第1步对E处理 | 第2步对E’处理 | 第3步对T处理 | 第4步对T’处理 | 第5步对F处理 | 第6步对E再处理 | 第7步对T再处理 | 之后继续几次直到发现各集合大小都不变 |
---|---|---|---|---|---|---|---|---|
F O L L O W ( E ) = FOLLOW(E)= FOLLOW(E)= | { # , \{\#, {#, | { # , \{\#, {#, | { # , \{\#, {#, | { # , \{\#, {#, | { # , ) , \{\#,), {#,), | { # , ) , \{\#,), {#,), | { # , ) , \{\#,), {#,), | { # , ) } \{\#,)\} {#,)} |
F O L L O W ( E ′ ) = FOLLOW(E')= FOLLOW(E′)= | { # , \{\#, {#, | { # , \{\#, {#, | { # , \{\#, {#, | { # , \{\#, {#, | { # , \{\#, {#, | { # , ) , \{\#,), {#,), | { # , ) , \{\#,), {#,), | { # , ) } \{\#,)\} {#,)} |
F O L L O W ( T ) = FOLLOW(T)= FOLLOW(T)= | { + , # , \{+, \#, {+,#, | { + , # , \{+, \#, {+,#, | { + , # , \{+, \#, {+,#, | { + , # , \{+, \#, {+,#, | { + , # , \{+, \#, {+,#, | { + , # , ) , \{+, \#,), {+,#,), | { + , # , ) , \{+, \#,), {+,#,), | { + , # , ) } \{+, \#,)\} {+,#,)} |
F O L L O W ( T ′ ) = FOLLOW(T')= FOLLOW(T′)= | { + , # , \{+, \#, {+,#, | { + , # , \{+, \#, {+,#, | { + , # , \{+, \#, {+,#, | { + , # , \{+, \#, {+,#, | { + , # , ) \{+, \#,) {+,#,) | { + , # , ) } \{+, \#,)\} {+,#,)} | ||
F O L L O W ( F ) = FOLLOW(F)= FOLLOW(F)= | { ∗ , + , # , \{*,+,\#, {∗,+,#, | { ∗ , + , # , \{*,+,\#, {∗,+,#, | { ∗ , + , # , \{*,+,\#, {∗,+,#, | { ∗ , + , # , \{*,+,\#, {∗,+,#, | { ∗ , + , # , ) , \{*,+,\#,), {∗,+,#,), | { ∗ , + , # , ) } \{*,+,\#,)\} {∗,+,#,)} |
第8章-自上而下分析2
8.1 构造递归下降分析器
8.1.1 递归下降分析器
- 分析程序由一组子程序组成,对每一 语法单位 (非终结符)构造一个相应的 子程序 ,识别对应的 语法单位
- 通过子程序间的相互调用实现对输入串的识别,例如,A → B c D 就有三个处理A、B、D的子程序
- 文法的定义通常是递归的,通常具有递归结构
定义全局过程和变量
ADVANCE
,把输入串指示器IP指向下一个输入符号, 即读入一个单词符号SYM
,IP当前所指的输入符号ERROR
,出错处理子程序
每个非终结符有对应的子程序的定义,在分析 过程中,当需要从某个非终结符出发进行展开 (推导)时,就调用这个非终结符对应的子程序
8.1.2 递归下降子程序设计
一个简单的子程序的设计
框内的代码不简化可以写成这样:
ELSE IF SYM ∈ FOLLOW(A) THEN
BEGIN END
ELSE ERROR
8.2 扩充的巴科斯范式和语法图
8.2.1 扩充的巴科斯范式
在元符号“→”或“::=”和“|”的基础上,扩 充几个元语言符号:
- 用花括号 α {α} α 表示闭包运算 α ∗ α^* α∗ 。
- 用表示 α 0 n {α}_0^n α0n 可任意重复0次至n次。
- 用方括号 [ α ] [α] [α] 表示 { α } 0 1 \{α\}_0^1 {α}01,即表示 α α α 的出现可有可无(等 价于 α ∣ ε α|ε α∣ε )。
例如,通常的“实数”可定义为:
D e c i m a l → [ S i g n ] I n t e g e r . { d i g i t } [ E x p o n e n t ] E x p o n e n t → E [ S i g n ] I n t e g e r I n t e g e r → d i g i t { d i g i t } S i g n → + ∣ − \begin{aligned} & Decimal→[Sign]Integer.\{digit\}[Exponent] \\ & Exponent→E[Sign]Integer \\ & Integer→digit\{digit\} \\ & Sign→ + |- \\ \end{aligned} Decimal→[Sign]Integer.{digit}[Exponent]Exponent→E[Sign]IntegerInteger→digit{digit}Sign→+∣−
用扩充的巴科斯范式来描述语法,直观易懂, 便于表示左递归消去和因子提取。
8.2.2 语法图
可以将最上面的文法转化为扩充巴科斯范式以及对应的语法图:
这样转化后的文法对应的递归子程序可以写成:
8.3 JavaCC简介
一个语法分析的小工具:
第9讲 语法分析——自上而下分析3
9.1 预测分析程序
9.1.1 预测分析程序的工作原理
预测分析程序构成
总控程序
,根据现行栈顶符号和当前输入符号, 执行动作分析表M[A, a]矩阵
, A ∈ V N , a ∈ V T A ∈V_N,a ∈V_T A∈VN,a∈VT 是终 结符或 ′ # ′ '\#' ′#′分析栈STACK
用于存放文法符号
预测分析过程
总控程序根据当前栈顶符号X和输入符号a,执行下列三动作之一:
- 若X=a=‘#’,则宣布分析成功,停止分析。
- 若X=a ≠‘#’,则把X从STACK栈顶逐出,让a指向 下一个输入符号。
- 若X是一个非终结符,则查看分析表M。
- 若M[X,a]中存放着关于X的一个产生式,把X逐出 STACK栈顶,把产生式的右部符号串按反序一一推进 STACK栈(若右部符号为ε,则意味不推什么东西进栈)。
- 若M[X,a]中存放着“出错标志”,则调用出错诊察程序ERROR。
总控程序实现
预测分析示例
对于上面的文法的一个预测分析的过程,其中分析表的构造看下一讲:
9.2 预测分析表的构造
9.2.1 分析表M[A,a]的构造
构造分析表前,首先要构造FIRST(α)和FOLLOW(A)
分析表M[A,a]的构造算法
构造G的分析表 M [ A , a ] M[A,a] M[A,a] ,确定每个产生式 A → α A→α A→α 在表中的位置:
- 1 对文法G的每个产生式 A → α A→α A→α 执行第2步和第3步;
- 2 对每个终结符 a ∈ F I R S T ( α ) a ∈FIRST(α) a∈FIRST(α) ,把 A → α A→α A→α 加至 M [ A , a ] M[A, a] M[A,a] 中;
- 3 若 ε ∈ F I R S T ( α ) ε∈FIRST(α) ε∈FIRST(α) ,则对任何 b ∈ F O L L O W ( A ) b∈FOLLOW(A) b∈FOLLOW(A) 把 A → α A→α A→α 加至 M [ A , b ] M[A,b] M[A,b] 中。
- 4 把所有无定义的 M [ A , a ] M[A,a] M[A,a] 标上“出错标志”。
9.2.2 LL(1)文法与二义性
- 如果G是左递归或二义的,那么,M至少含有 一个多重定义入口。因此,消除左递归和提取 左因子将有助于获得无多重定义的分析表M。
- 可以证明,一个文法G的预测分析表M不含多 重定义入口,当且仅当该文法为LL(1)的。
- LL(1)文法不是二义的。
例如这样一个实现 ifelse 语句的文法就是二义性的,解决方法是人为的确定一个ifelse 语法,如就近匹配选择 S ′ → e S S'\to eS S′→eS
(end)
https://blog.csdn.net/pi31415926535x/article/details/105163345