编译原理_复习笔记4章

第四章 自上而下语法分析

上下文无关文法–语法分析依靠的文法

语法分析的任务:分析一个文法的句子结构。(判断一个串是不是一个文法所描述的句子,是不是能由这个文法的开始符号推导出来)

语法分析器的功能:按照文法的产生式(语言的语法规则),识别输入符号串是否为一个句子(形式上正确的程序)。

要进行语法分析,必须对语言的语法结构进行描述。

  • 采用正规式和有限自动机可以描述和识别语言的单词符号;
  • 用上下文无关文法来描述语法规则。

语法分析的任务是分析一个文法的句子结构。

语法分析器的功能:按照文法的产生式(语言的语法规则),识别输入符号串是否为一个句子。

  • 这里所说的输入串是指由单词符号(文法的终结符)组成的有限序列。
  • 对一个文法,当给你一串符号时,怎样知道它是不是该文法的一个句子(程序)呢?这就要判断,看是否能从文法的开始符号出发推导出这个输入串,或建立一棵与输入串匹配的语法树。

自上而下分析法(Top-down)

  • 基本思想:它从文法的开始符号出发,反复使用各种产生式,寻找”匹配”(输入字符串)的 推导。
    • 对任何输入串,试图用一切可能的办法,从文法开始符号(根结点)出发,自上而下地为输入串建立一棵语法树。
    • 或者说,为输入串寻找一个最左推导。
  • 递归下降分析程序–对每一语法变量(非终结符)构造一个相应的子程序,每个子程序识别一定的语法单位;通过子程序间的相互调用实现对输入串的识别。
  • 预测分析程序–优点:直观、简单和宜于手工实现。

面临的问题:

  • 当某个非终结符有多个产生式候选时,在分析过程中,当一个非终结符用某一个候选匹配成功时,这种匹配可能是暂时的。出错时,不得不“回溯”。
  • 文法左递归问题:含有左递归的文法(P =+=> Pα,P ∈ Vn)将使自上而下的分析陷入无限循环。如E->E+T则归约时E总是被替换为E+T,E又重新出现了,死循环!

问题的解决:LL(1)分析法

构造不带回溯的自上而下分析算法:消除文法的左递归性并克服回溯

左递归的消除:若产生式P -> Pα | ß 其中ß不以P开头,可以把P的规则等价改写为非直接左递归形式:P -> ßP‘;P’ -> αP‘ | ℇ【理论支持:P -> Pα -> Pαα -> … -> ßα … α】

同时记住还需要确认不含回路如P =+=> P,因为有的文法表面上不含有直接左递归但推导出来就含有左递归了:

例 考虑文法G(S)

S -> Qc|c

Q -> Rb|b

R -> Sa|a

S -> Qc -> Rbc -> S abc 存在左递归

  • 令它的非终结符的排序为R、Q、S(随意排序,最终所得文法是等价的)。
  • 对于R,不存在直接左递归。
  • 把R代入到Q的有关候选后,把Q的规则变为
    • Q -> Sab | ab | b
  • 现在的Q不含直接左递归,把它代入到S的有关候选后,S变成
    • S -> Sabc | abc | bc | c
  • 消除S的直接左递归后:
    • S -> abcS‘ | bcS‘ | cS‘
    • S‘ -> abcS‘ | ℇ
    • Q -> Sab |ab | b
    • R -> Sa|a
  • 关于Q和R的规则已是多余的(因为属于从开始符号S出发永远无法到达的非终结符的产生式,故应化简掉),化简为:
    • S -> abcS‘ | bcS‘ | cS‘
    • S‘ -> abcS‘ | ℇ

用白话来讲就是通过不断把间接左递归文法句型中的所有非终结符(如上面的R、Q、S)最终都用其中的一个非终结符做代表(如S),这样就化间接为直接了,然后就可以用直接左递归的化简方法。

综上,消除一个文法的一切左递归的条件:

  1. 不含以ℇ为右部的产生式(但改写后的文法可能含以ℇ为右部的产生式,这个是OK的)
  2. 不含回路:P =+=> P

克服回溯:必须保证对文法的任何非终结符,当要它去匹配输入串时,能够根据它所面临的输入符号准确地指派它的一个候选去执行任务,并且此候选的工作结果应是确信无疑的。FIRST和FOLLOW集。

  • FIRST(α)指从α所能推导出的所有串的首终结符(起始终结符)的集合
    • 需要达到任何两个不同候选 αi 和 αj的FIRST(αi) ∩ FIRST(αj)= ∅,也就是不想交,所谓的唯一性
    • 如果达不到上面的条件,那就需要提取左公共因子。具体的,如果α的终结首符集有共同的部分(如A -> δ ß1 | δ ß2 | … | δ ßn | γ1 | γ2 | … | γm),可提取其公共左因子A -> δA’ | γ1 | γ2 | … | γm;A‘ -> ß1 | ß2 | … | ßn;经过反复提取左因子,就能够把每个非终结符(包括新引进者)的所有候选首符集变成为两两不相交。
    • 对于去除左递归后产生的如A -> ℇ的产生式,在匹配时能否自动使用的问题,需要判断当前输入符号是否在紧跟在A后面的终结符集FOLLOW(A)中,如不在,当前输入符号的出现就是语法错误。
    • 若最后推出了ℇ,也就是“推导推导推导推成空了。推没了”,一样也把 ℇ 加到FIRST集中
  • FOLLOW(A)是由所有句型中所有有可能紧跟在A后面的终结符a们所组成的集合

    • 适用情况:如果要把一个非终结符替换成 ℇ ,好让这个非终结符后面的去识别你当前非终结符的产生式识别替换不了的单词符号,那前提条件就需要这个当时替换不了的单词符号是这个非终结符的FOLLOW集中的元素,这时此非终结符才能被替换为 ℇ ,然后把无法替换的这个单词符号交给此非终结符后面的符号去匹配。
    • 特别是,若S =*=> …A(若S能推出以A结尾的串),则规定# ∈ FOLLOW(A) //句子以 # 开始,以 # 结束
      (Copyright © https://blog.csdn.net/s_gy_zetrov. All Rights Reserved)
      综上上,构造不带回溯的自上而下分析的文法条件

      1. 文法不含左递归
      2. 对于文法中每一个非终结符A的各个产生式的候选首符集两两不相交。即,若A -> α1| α2 | … | αn 则 FIRST(αi) ∩ FIRST(αj)= ∅ (i != j)
      3. 对文法中的每个非终结符A,若它存在某个候选首符集包含ℇ,则FIRST(αi) ∩ FOLLOW(A) = ∅ (i=1,2,…,n) 即如果存在 ℇ 则不光需要FIRST集和FIRST集两两不相交,还需要FIRST和FOLLOW不能相交。如果不含 ℇ ,则此条不算。

如果一个文法G满足以上条件,则称该文法G为LL(1)文法。

  • 第一个L:从左到右扫描输入串
  • 第二个L:最左推导
  • 1:分析时每一步只需向前查看一个符号

对于一个满足上述条件的文法,可以对其输入串进行有效的无回溯的自上而下分析。假设要用非终结符A进行匹配,面临的输入符号为a,A的所有产生式为
A -> α1 | α2 | … | αn
1. 若a ∈ FIRST(αi),则指派 αi 执行匹配任务;
2. 若a不属于任何一个候选首符集,则:
- 若 ℇ 属于某个FIRST(αi)且 a ∈ FOLLOW(A),则让 A 与 ℇ 自动匹配。
- 否则,a的出现是一种语法错误。

也就是说,对于一个LL(1)的文法,有且只能有以上三种情况中的一种发生,要么指派 αi 执行匹配任务、要么则让 A 与 ℇ 自动匹配、要么返回语法错误。

那么再来回顾一下FIRST和FOLLOW集合的定义:

  • FIRST(α)指从α所能推出的所有串中排在第一个位置的终结符(首终结符、起始终结符)构成的集合
  • FOLLOw(A)指在任何一个句型里面能够跟在A后面的终结符构成的集合

用自己的话总结FIRST、FOLLOW集求法、预测分析表的构造

FIRST集合:反复执行1、2、3直到每个FIRST都不再变化

  1. 针对终结符X,FIRST(X) = {X}
  2. 直接检查每个产生式,
    • 产生式是x -> ℇ,ℇ要加入FIRST(X)中
    • 开头是终结符的话,把终结符加到FIRST集中

    • 产生式右边有多个符号,前面若干个符号能推出 ℇ ,则紧接着的那个符号的FIRST集中元素要进入到被定义的FIRST集中
    • 产生式右边的所有符号的FIRST集均包含 ℇ ,则 ℇ 进入产生式左边的FIRST集中(A -> BCD, ℇ ∈ FIRST(B)、FIRST(C)、FIRST(D) => ℇ ∈ FIRST(A))
    • 产生式右边首个符号是非终结符,则该符号的FIRST集除去ℇ后,要进入产生式左边符号的FIRST集中(X -> ABC => FIRST(A)/{ℇ} ∈ FISRT(X))

FOLLOW集合:ℇ 永远不进FOLLOW集

  1. 对于文法的开始符号,‘#’要进入其FOLLOW集中,然后对每个产生式逐条检查规则2、3
  2. A -> αBß,FIRST(ß)/{ℇ} ∈ FOLLOW(B)。还可以这样看:A -> BC,C在B后面,那C的FIRST(ß)/{ℇ}一定在B的FOLLOW中
  3. A -> αB 或 A -> αBß且 ℇ ∈ FIRST(B),则FOLLOW(A) ∈ FOLLOW(B)

预测分析表的构造:文法中每个产生式应该放在哪个格子里

  • 对于非终结符A,它的所有产生式都应该放在“A”那一行,但是放在哪一列,有讲究。如对于A -> α中α这个候选,“A -> α”应该放在FIRST(α)中元素所在的列里
  • 若 ℇ ∈ FIRST(α),则应把A->α放在A那一行,FOLLOW(A)中元素所在的列和FIRST(A)/{ℇ}中元素所在的列里
  • 其它情况基本就是放在FIRST(A)中

预测分析程序

  • 特征:根据当前输入符号,为当前要处 理的非终结符选择产生式
  • 要求:文法是LL(1)的

如果G是左递归或二义的,那么,M至少含有一个多重定义入口。因此,消除左递归和提取左因子将有助于获得无多重定义的分析表M。一个文法G的预测分析表M不含多重定义入口,当且仅当该文法为LL(1)的。

(Copyright © https://blog.csdn.net/s_gy_zetrov. All Rights Reserved)


visitor tracker
访客追踪插件


猜你喜欢

转载自blog.csdn.net/S_gy_Zetrov/article/details/81751139