自底向上分析概述

  • 从分析树的底部 (叶节点) 向顶部 (根节点) 方向构造分析树
  • 可以看成是将输入串 w 归约为文法的开始符号 S 的过程
  • 的语法分析采用最左推导方式
  • 的语法分析采用最左归约方式 (反向构造最右推导)
  • 自底向上语法分析的通用框架:移入 - 归约分析 (Shift-Reduce Parsing)

【例】移入 - 归约分析
image.png

  • $表示栈底和栈顶
  • 开始时栈中没有任何符号
  • 第一步,将id入栈
  • 第二步,因为id是第四个产生式的右部,所以将id归约为E,id出栈,E入栈
  • ……
  • 每次归约的符号串称为 “句柄”,一旦句柄在栈顶出现,我们就立即将其归约,从而保证我们的每一次归约都是最左归约
  • 栈内符号串 + 剩余输入 = 规范句型

    移入 - 归约分析的工作过程

  • 在对输入串的一次从左到右扫描过程中,语法分析器将零个或多个输入符号移入的顶端,直到它可以对栈顶的一个文法符号串β进行归约为止

  • 然后,它将β归约为某个产生式的左部
  • 语法分析器不断地重复这个循环,直到它检测到一个语法错误,或者栈中包含了开始符号且输入缓冲区为空 (当进入这样的格局时,语法分析器停止运行,并宣称成功完成了语法分析) 为止

移入 - 归约分析器可采取的 4 种动作:

  • 移入:将下一个输入符号移到栈的顶端
  • 归约:被归约的符号串的右端必然处于栈顶。语法分析器在栈中确定这个串的左端,并决定用哪个非终结符来替换这个串
  • 接收:宣布语法分析过程成功完成
  • 报错:发现一个语法错误,并调用错误恢复子例程

【例】移入 - 归约分析中存在的问题
image.png

  • 句柄var,i既可以使用产生式 2 归约,又可以使用产生式 3 归约,由于句柄是句型的最左直接短语,所以选用产生式 3 进行归约

    LR 分析法概述

  • LR 文法 (Knuth, 1963) 是最大的、可以构造出相应移入 - 归约语法分析器的文法类

    • L: 对输入进行从到右的扫描
    • R: 反向构造出一个最推导序列
  • LR(k) 分析

    • 需要向前查看 k 个输入符号的 LR 分析
    • k = 0 和 k = 1 这两种情况具有实践意义
    • 当省略 (k) 时,表示 k =1

      LR 分析法的基本原理

  • 自底向上分析的关键问题是什么?

    • 如何正确地识别句柄
  • 句柄是逐步形成的,用 “状态” 表示句柄识别的进展程度

【例】状态
image.png

  • 文法开始符号 S 定义为 bBB,因为产生式右部由 3 个符号构成,所以将句柄识别划分为 4 个状态
  • S→·bBB 移进状态:期待出现一个终结符 b,当终结符 b 出现,将其移入分析栈中,进入下一状态
  • S→b·BB 待约状态:期待归约出一个非终结符 B,当归约出非终结符 B,进入下一状态
  • S→bB·B 待约状态:期待归约出一个非终结符 B,当归约出非终结符 B,进入下一状态
  • S→bBB· 归约状态:此时构成句柄的三个符号都已识别,将其归约成文法开始符号 S
  • LR 分析器基于这样一些状态来构造自动机进行句柄的识别

    LR 分析器 (自动机) 的工作过程

    image.png

  • LR 分析器 (自动机) 由输入带、读头、带有分析表的主控程序和状态、符号栈构成

【例】LR 分析表的结构
image.png

  • 状态有 7 个0-6、ACTION 表中有 2 个终结符a、b和一个结束符号$、GOTO 表中有两个非终结符S、B
  • Sn:将符号 a/b 压入符号栈,状态 n 压入状态栈
  • rn:用第 n 个产生式进行归约,对应终结符和状态出栈,非终结符入栈
  • GOTO 表中的每一项表示在某一状态遇到某一非终结符时,进入的后继状态

【例】LR 分析器的工作过程。若输入串 bab,则 LR 分析器的工作过程如下:

  • 初始时,将待分析的输入串放入输入缓冲区中,后面连接结束符$。状态栈中只有状态0,符号栈中只有栈底标记$:

image.png

  • 初始状态 0 遇到终结符 b,查 ACTION 表,采取 S4,将符号 b 压入符号栈,状态 4 压入状态栈:

image.png

  • 状态 4 遇到终结符 a,查 ACTION 表,采取 r3,用第 3 个产生式进行归约,对应终结符 b 和状态 4 出栈,非终结符 B 入栈;

image.png

  • 状态 0 遇到非终结符 B,查 GOTO 表,状态 2 入栈

image.png

  • 状态 2 遇到终结符 a,查 ACTION 表,采取 S3,将符号 a 压入符号栈,状态 3 压入状态栈:

image.png

  • ……

image.png

  • 状态 6 遇到结束符$,查 ACTION 表,采取 r2,用第 2 个产生式进行归约,对应终结符 aB 和状态 36 出栈,非终结符 B 入栈:

image.png

  • 状态 2 遇到非终结符 B,查 GOTO 表,状态 5 入栈:

image.png

  • 状态 5 遇到结束符$,查 ACTION 表,采取 r1,用第 1 个产生式进行归约,对应非终结符 BB 和状态 25 出栈,非终结符 S 入栈:

image.png

  • 状态 0 遇到非终结符 S,查 GOTO 表,状态 1 入栈:

image.png

  • 状态 1 遇到结束符$,查 ACTION 表,acc 表示 accept,成功将该输入串归约为文法的开始符号 S

    LR 分析算法

    CLOSURE 函数

    image.png

    GOTO 函数

    image.png image.png
    如何构造给定文法的 LR 分析表?

  • 不同的 LR 分析法有不同的 LR 分析表构造方法

    • LR(0) 分析
    • SLR 分析
    • LR(1) 分析
    • LALR 分析

      LR(0) 分析

      右部某位置标有圆点的产生式称为相应文法的一个 LR(0) 项目(简称为项目),LR(0) 项目的一般形式:A→α1·α2Aα_1⋅α_2
      image.png
  • 若一个产生式右部有 k 个符号,则对应有 k+1 个项目

    增广文法

    image.png

    文法中的项目

    image.png

  • 这 15 个项目中有某些项目是等价的

  • 可以把等价的项目组成一个项目集 (I) ,称为项目集闭包 (Closure of Item Sets),每个项目集闭包对应着自动机的一个状态
  • 项目 0 表示,等待非终结符 S;由产生式 2 可知,等待非终结符 S 相当于等待终结符 v,也就是项目 2;所以项目 0 和项目 2 等价
  • 同理,项目 3、项目 7 和项目 11 等价;项目 5 和项目 13 等价
  • 综上,当圆点后面是一个非终结符时,说明存在等价项目

【例】根据文法构造 LR(0) 分析的分析表
image.png

  • 文法→分析图→分析表
  • 将初始项目 S’→·S 放到初始状态 I0 中,初始项目在等待非终结符 S,由产生式 2 可知,等待非终结符 S 就相当于在等待非终结符 B,因此,将初始项目的等价项目 S→·BB 加入状态 I0,同理,加入其他等价项目
  • 根据状态 I0 中的第一个项目:当归约出非终结符 S 时,状态 I0 进展到状态 I1,将项目 S’→·S 的后继项目 S’→S· 加入状态 I1,
    • 由于项目 S’→S· 是归约项目,且文法开始符号出现在产生式右部,所以状态 I1 是接收状态,遇到结束符$时,acc
  • 根据状态 I0 中的第二个项目:当归约出非终结符 B 时,状态 I0 进展到状态 I2,将项目 S→·BB 的后继项目 S→B·B 加入状态 I2,由于圆点后面是一个非终结符,所以要加入其他等价项目
  • 根据状态 I0 中的第三个项目:当归约出终结符 a 时,状态 I0 进展到状态 I3,将项目 B→·aB 的后继项目 B→a·B 加入状态 I3,由于圆点后面是一个非终结符,所以要加入其他等价项目
  • 根据状态 I0 中的第四个项目:当归约出终结符 b 时,状态 I0 进展到状态 I4,将项目 B→·b 的后继项目 B→b· 加入状态 I4
    • 由于项目 B→b· 是归约项目,所以状态 I4 是遇到任何符号都使用产生式 3 进行归约
  • 状态 I1 中的唯一一个项目是归约项目,因为圆点后不再有符号,所以不再有后继状态
  • 根据状态 I2 中的第一个项目:当归约出非终结符 B 时,状态 I2 进展到状态 I5,将项目 S→B·B 的后继项目 S→BB· 加入状态 I5
    • 由于项目 S→BB· 是归约项目,所以状态 I5 遇到任何符号都使用产生式 1 进行归约
  • 根据状态 I2 中的第二个项目:当归约出终结符 a 时,状态 I2 进展到状态 I3
  • 根据状态 I2 中的第三个项目:当归约出终结符 b 时,状态 I2 进展到状态 I4
  • 根据状态 I3 中的第一个项目:当归约出非终结符 B 时,状态 I3 进展到状态 I6,将项目 B→a·B 的后继项目 B→aB· 加入状态 I6
    • 由于项目 B→aB· 是归约项目,所以状态 I6 遇到任何符号都使用产生式 2 进行归约
  • 根据状态 I3 中的第二个项目:当归约出终结符 a 时,状态 I3 仍是状态 I3
  • 根据状态 I3 中的第三个项目:当归约出终结符 b 时,状态 I3 进展到状态 I4
  • 状态 I5 中的唯一一个项目是归约项目,因为圆点后不再有符号,所以不再有后继状态
  • 状态 I6 中的唯一一个项目是归约项目,因为圆点后不再有符号,所以不再有后继状态

    LR(0) 分析中的冲突

    移进 / 归约冲突

    image.png image.png

    移进 / 归约冲突和归约 / 归约冲突

    image.png

  • 如果 LR(0) 分析表中没有语法分析动作冲突,那么给定的文法就称为 LR(0) 文法

  • 不是所有 CFG(上下文无关文法) 都能用 LR(0) 方法进行分析,也就是说,CFG 不总是 LR(0) 文法
  • 如何消解冲突将在后面的 SLR 分析法和 LR(1) 分析法中介绍

    SLR 分析

    image.png
    【例 1】用 SLR 分析法的基本思想解决 LR(0) 分析过程中的移进 / 归约冲突
    image.png

  • 在状态 2,当下一个输入符号是*时,既可以采取归约,也可以采取移入,产生冲突

  • 该问题本质是如何识别句柄,如果栈顶符号 T 是一个句柄,就应该归约,否则就不应该归约,可见 LR(0) 本身的信息已经不能帮助我们确定是否进行归约
  • 事实上,LR(0) 分析在构造项目的时候,没有向前查看符号,也就是没有考虑文法符号的上下文环境
  • 在状态 2,当下一个输入符号是时,如果把栈顶的 T 归约成 E,根据 FOLLOW(E) 可知,E 后面不可能连接,所以这里不应该归约,T 不是句柄
  • 由此可见,FOLLOW 集可以帮助判断是否进行归约

image.png

  • 用 SLR 分析法的基本思想解决 LR(0) 分析过程中的冲突后得到上面的分析表
  • 与 LR(0) 分析法得到的分析表相比,SLR 分析法得到的分析表中的归约状态只在遇到 FOLLOW 集中的元素才采取归约;而 LR(0) 分析法得到的分析表中的归约状态在遇到任何元素都采取归约

【例 2】用 SLR 分析法的基本思想解决 LR(0) 分析过程中的移进 / 归约冲突和归约 / 归约冲突
image.png

  • 在状态 2,既可以将栈底的空串归约为 B,也可以归约为 T,产生冲突;此外,还有一个移入项目,也与两个归约项目产生冲突
  • 对于状态 2 中的归约归约冲突,查看 FOLLOW 集可知,FOLLOW(B) 中只有 d,因此,状态 2 遇到 d 时采用第四个产生式;FOLLOW(T) 中有 b 和$,因此,状态 2 遇到 b 和$时采用第二个产生式;此外,状态 2 遇到 a 时采用移入动作,将 a 入栈,进入状态 2

如果给定文法的 SLR 分析表中不存在有冲突的动作,那么该文法称为 SLR 文法

SLR 分析中的冲突

image.png

  • 状态 2 中,第一个项目表示,当下一个输入符号是=时,采用移入动作,将=入栈,进入状态 6;第二个项目表示,当遇到 FOLLOW(R) 集中的元素=和$时,将栈顶元素归约为 R。因此,当遇到=时,产生移入 / 归约冲突。
  • 可见,仅依靠 FOLLOW 集不能完全解决冲突

    LR(1) 分析

    SLR 分析存在的问题:

  • SLR 只是简单地考察下一个输入符号 b 是否属于与归约项目 A→α相关联的 FOLLOW(A),但 b∈FOLLOW(A) 只是归约α的一个必要条件,而非充分条件

    LR(1) 项目

    image.png

  • ,后面的叫展望符

【例】LR(1) 项目的表示
image.png

  • LR(1) 文法在构造项目的时候,将特定位置的后继符信息考虑了进去
  • 在 R 位置,R 的后继符号是$,采用R→L·,S来表示项目:R 产生式的归约项目,这个项目表示:当下一个符号是$时,可以将栈顶的 L 归约为 R

    等价 LR(1) 项目

    image.png

  • 展望符是 FIRST(β) 集中元素

【例】构造 LR(1) 分析表
image.png image.png

  • I0 中第一个项目,因为 S’后面只能跟结束符$,所以该项目的展望符是$
  • I0 中第二三个项目,是第一个项目的等价项目,因为第一个项目的 S 后面是一个空串,所以它的两个等价项目继承展望符$
  • I0 中第四五个项目,是第二个项目的等价项目,因为第二个项目的 L 后面是一个 =,所以它的两个等价项目继承展望符 =
  • ……
  • 与 SLR 分析法和 LR(0) 分析法得到的分析表相比,LR(1) 分析法得到的分析表中的归约状态只在遇到展望符时才采取归约;而 SLR 分析法得到的分析表中的归约状态在遇到 FOLLOW 集中的元素采取归约;LR(0) 分析法得到的分析表中的归约状态在遇到任何元素都采取归约
  • 如果除展望符外,两个 LR(1) 项目集是相同的,则称这两个 LR(1) 项目集是同心