编译原理第四章 语法分析——自上而下分析

知识总结

一、自上而下分析

1、基本思想

    对任何一个输入串试图用一切可能的办法,从文法的开始符号(根节点)出发,根据文法自上而下地为输入串建立一棵语法树,即为输入串寻找一个最左推导。

2、本质:是一种试探过程,是反复使用不同产生式谋求匹配输入串的过程。

3、实现方法

    让每个非终结符号对应一个递归子程序。每个子程序可以作为一个布尔过程(返回“真”或“假”):

         (1)一旦发现它的某个候选与输入串相匹配,就用这个候选式去扩展语法树,并返回“真”值;

         (2)否则,保持原来的语法树和IP值不变(IP回溯),并返回“假”值。

4、存在问题

  • 文法的左递归性问题
  • 回溯的不确定性
  • 虚假匹配的问题
  • 不能准确地确定输入串中出错的位置
  • 穷尽一切可能的试探法,效率低代价高

二、消除左递归

  • 对于直接左递归,如产生式P→Pα1|Pα2|…|Pαm|β1|β2|…|βn(其中每个βi不以P开头,每个αi不为ε),改写成:

       P→β1P’|β2P’|…|βnP’       P’→α1P’| α2P’|…|αmP’| ε

  • 对于间接左递归,按消除算法执行,注意非终结符排列顺序不同,消除递归之后文法的形式也可能不同,但表达能力相同。

      消除左递归算法:

   (1)把文法G的所有非终结符按任意顺序排序为P1,P2,P3……;按此顺序执行

   (2)FOR i:=1 TO  n {

                FORj=1 TO i-1 {

                          把形如Pi->Pjγ的规则改写成:Pi->δ1γ| δ2 γ|…| δk γ(其中Pj->δ1 | δ2|…| δk是关于Pj的所有规则)

                }     

               消除关于Pi规则的直接左递归;

          }

   (3)去除无用(从起始符永远不能到达)的非终结符的产生式

  • 消除左递归(一般方法)

    (1)将间接左递归改造为直接左递归

            将文法中所有如下形式的产生式:Pi→Pjγ|β1|β2|…|βn    Pj→δ1|δ2|δ3|…|δk

            改写成: Pi →δ1γ|δ2γ|δ3γ|…|δkγ|β1|β2|…|βn

   (2)消除直接左递归

   (3)化简改写后的文法,即去除那些从开始符号出发永远无法到达的非终结符的产生规则。

三、消除回溯

      对文法的任何非终结符,当要它去匹配输入串时,能够根据该非终结符所面临的输入符号准确地指派它的一个候选式去匹配,并且此候选式匹配后得到的工作结果应该是确信无疑的,即:

      (1)若该候选式匹配成功,那么该匹配不是虚假匹配;

      (2)若该候选式无法完成最终的匹配任务,则其他任何候选式肯定也无法完成。

  • 定义FIRST集

      文法G是不含左递归的文法,对G的所有非终结符的候选α,定义它的终结首符(开始符号)集合:

      FIRST(α)={a|α=>a…,a∈V},特别地,如果α=>ε,则ε∈FIRST(α)

      即:FIRST(α)是α的所有可能推导的开头终结符或可能的ε。

      如果非终结符A的任意两个候选式αi和αj的开始符号集满足FIRST(αi)∩FIRST(αj)=Φ,

      则A可根据所面临的第一个输入符号准确地指派一个候选式α去执行任务,α是那个FIRST集含a的候选式,即a ∈FIRST(α)。

  • 改造文法:提取公共左因子

      假设A的产生式为A→δβ1|δβ2|…|δβn|γ1| γ2|…|γm,其中每个γ不以δ开头

      那么把这些产生式改写为: A→δA’ |γ1| γ2|…|γm    A’→β1|β2|…|βn

      经过反复提取左因子(包括新引进的非终结符),就能把每个非终结符(包括新引进的)的所有候选首符集变成两两不相交。

四、LL(1)分析法

      (L:从左到右扫描  L:最左推导  1:分析时每一步只看一个符号)

  • 定义FOLLOW集

       对文法G的任何非终结符A,定义它的后继符号集合:FOLLOW(A)={a|S=>…Aa…,a∈VT}。特别地,如果S=>…A,则#∈FOLLOW(A)

       FOLLOW(A)集合是所有句型中出现在紧接A之后的终结符号或#所组成的集合

       当非终结符A面临输入符号a且a不属于A的任意候选式的FIRST集但A的某个候选式的FIRST集包含ε时,

       只有当a ∈FOLLOW(A),才可能允许A自动匹配。

 1、不带回溯的自上而下分析的文法条件(LL(1)文法的判断)

    (1)文法不含左递归;

    (2)对于文法中每一个非终结符A的各个产生式的候选式的FIRST集两两不相交;

        即若A→α1|α2|…|αn,则FIRST(αi)∩FIRST(αj)=Φ (i≠j)

   (3)对于文法中的每个非终结符A,若它的某个候选首符集包含ε,则FIRST(A)∩FOLLOW(A)=Φ。

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

2、根据LL(1)文法分析方法:

    假设要用非终结符A进行匹配,面临输入符号为a,A的所有产生式为A→α1|α2|…|αn

   (1)若a∈FIRST(αi) ,则指派αi去匹配;

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

       ①若ε属于某个FIRST(αi)且a∈FOLLOW(A),则让A与ε自动匹配;

       ②否则,a的出现是一种语法错误。

五、递归下降分析程序构造

     当一个文法满足LL(1)条件时,我们就可以构造一个不带回溯的自上而下分析程序。

     这个分析程序由一组(可能的)递归程序组成,每个过程对应文法的一个非终结符。

     这样一个分析程序称为递归下降分析器。

  •  实现方法:对文法的每一个非终结符都编一个分析程序,当根据文法和当时的输入符号预测到要用某个非终结符去匹配输入串时,就调用该非终结符的分析程序。
六、预测分析程序

实现LL(1)分析的一种有效方式是使用一张分析表和一个栈进行联合控制。预测分析程序就是属于这种类型的LL(1)分析器。

  • 分析表:指导分析过程中候选式的选取
  • 符号栈(分析栈):四种状态:开始状态、工作状态、出错状态、结束状态
  • 执行程序 (总控程序)

七、LL(1)分析中的错误处理

1、在预测分析过程中,出现下列两种情况则说明遇到了语法错误:

     (1)栈顶的终结符与当前的输入符不匹配;

     (2)非终结符A处于栈顶,面临的输入符号为a, 但分析表中M[A,a]为空。

 2、错误恢复的方法:跳过输入串中的一些符号直到遇到“同步符号”为止。遇到同步符号时,将符号栈顶的非终结符出栈。

 3、“同步符号”的选择:

  • 将FOLLOW(A)设为同步符号
  • 将FIRST(A)加入到同步符号
  • 如果非终结符产生空串,可以自动匹配,以推迟检测到错误的时间。
  • 如果栈顶是终结符,当出错时,直接将栈顶出栈

课后习题

编译原理第四章 语法分析——自上而下分析_第1张图片 编译原理第四章 语法分析——自上而下分析_第2张图片

总结感悟

      语法分析是编译过程的核心,目的是判断一个输入串是否符合语法规则。判断方法有两种,一个是从文法的起始符出发进行句子的推导,即自上而下的分析;另一个是从句子本身出发进行归约,看能否把句子规约为到起始符,即自下而上的规约。语法分析最后的分析结果是构造一棵语法树。

      这一章的主要能容是语法分析的第一种方法,即自上而下分析。和前面的词法分析相比这一章的难度明显加大,通过消除左递归、消除回溯和提取左因子我们得到LL(1)文法条件,然后可以建立预测分析程序。预测分析程序包括分析表、符号栈和执行程序,其中分析表是本章的重点和难点。预测分析表构造时FIRST集相对好写,而FOLLOW集却很难分析,课上老师进行了重点的讲解,通过习题的锻炼可以帮助我们更好的理解分析表的构建问题。课程难度逐渐增加,课前预习就变得非常的重要,课前自己对课本知识有个大概了解,知道自己哪里理解不了,课上就可以有针对性学习。


你可能感兴趣的:(编译原理第四章 语法分析——自上而下分析)