编译原理笔记(三)之语法分析

1. 语法分析的若干问题

  • 分析树和语法树

  • 分析语法结构的基本方法:自上而下分析方法自下而上分析方法

  • 语法分析的双重含义:(定义规则和执行规则)

    • 规定句子形成的规则,也被称为语法规则,程序设计语言的大部分语法规则可以用==上下文无关文法(CFG)==来描述;(立法)
    • 根据语法规则标识记号流中的语言结构,也被称为语法分析。(执法)

1.1 语法分析器的作用

  1. 根据词法分析器提供的记号流,为语法正确的输入**构造分析树**(或语法树)
  2. 检查输入中的语法(可能包括词法)错误,并**调用出错处理器**进行适当处理

在这里插入图片描述

1.2 语法错误的处理原则

1.2.1 源程序中可能出现的错误

在这里插入图片描述

1.2.2 语法错误处理的目标

  1. 清楚而准确地报告错误的出现,地点正确,不漏报、不错报也不多报。
  2. 迅速地从每个错误中恢复过来,以便分析继续进行。
  3. 对语法正确源程序的分析速度不应降低太多。

1.2.3 语法错误的基本恢复策略

  1. 紧急方式恢复
    发现错误时,分析器每次抛弃一个输入记号,一直向前搜索,直到输入记号属于某个指定的合法文法记号(称为同步记号)集合为止。
  2. 短语级恢复
    采用串替换的方式对剩余输入进行局部纠正。
    典型的局部纠正是用分号代替逗号,删除多余分号,或者插入遗漏的分号等;
  3. 出错产生式
    用出错产生式捕捉错误。这是语法分析器生成器YACC采用的方式,它基本上可以被认为是一种预置型的短语级恢复方式。
  4. 全局纠正
    对有语法错误的输入序列x,根据文法G构造相近序列y的语法树,使得x变换成y所需的修改、插入、删除次数最少。代价太大。

2. 上下文无关文法

2.1 上下文无关文法的定义与表示

定义:上下文无关文法(CFG)是一个四元组G=(N,T,P,S),其中
(1)N是非终结符的有限集合
(2)T是终结符的有限集合,且N∩T=∅
(3)P是产生式的有限集合,每个产生式形如:A->α。其中A∈N,被称为产生式的左部;α∈(N∪T)*,被称为产生式的右部。若α=ε,则称A->ε为空产生式(也可以记为A->)
(4)S是非终结符,被称为文法的开始符号

  1. 由产生式集表示CFG
    由于每个产生式中具有A∈N且α∈(N∪T)*,所以,对于一个没有错误的CFG,可以这样区分N和T集合:
    N是可以出现在产生式左边的符号的集合
    T是词法分析器返回的记号的集合(某些约定的特殊终结符除外),根据N∩T=∅可以推断出T是所有不出现在产生式左边的符号的集合
    如果再约定S是第一个产生式的左部,则文法可以由其产生式集P代替,即不写四元组,而仅给出P。CFG的产生式表示也称为巴克斯范式(BNF)
    注意:规范的BNF中,“->”用“::=”表示。
  2. 产生式的一般读法
    “->”读作“定义为”或者“可导出”
  3. 终结符与非终结符书写上的区分
    一般情况
    • 非终结符:大写英文字母A、B、C表示
    • 终结符:小写字母a、b、c表示
    • 文法符号序列:小写希腊字符α、β、δ表示,即大小写混合的英文字母串
  4. 产生式的缩写形式
    把左部非终结符相同的产生式合并成一个产生式,所有产生式右部由或符号(|)连接,每一个右部现在被称为该产生式的一个候选项,各候选项具有平等的权利。
    例如:
<!--文法G3.2-->
E->E+E
  |E*E
  |(E)
  |-E
  |id

2.2 CFG产生语言的基本方法——推导

  • 可以通过推导的方法产生CFG所描述的语言。非正式地讲,推导就是从文法的开始符号S开始,反复使用产生式,将产生式的左部的非终结符替换成右部的文法符号序列(展开产生式,用标记=>表示),直到得到一个终结符序列(句子).

  • 栗子
    -(id+id)可以由文法G3.2产生
    E=>-E=>-(E)=>-(E+E)=>-(id+E)=>-(id+id)

  • 定义:将产生式A->γ的右部代替文法符号序列αAβ中的A得到αγβ的过程,称为αAβ直接推导出αγβ,记作:αAβ=>αγβ
    在这里插入图片描述
    定义
    在这里插入图片描述

2.3 推导、分析树与语法树

  1. 分析树(具体语法树)
    在这里插入图片描述
  2. 语法树(抽象语法树)
    在这里插入图片描述
    (a)是上述分析树对应的语法树
    (b)是条件语句if condition then s1 else s2 的语法树

2.4 二义性与二义性的消除

2.4.1 二义性

(1)一个句型有多于一颗分析树,仅与文法和句型有关,与采用的推导方法无关
(2)造成二义性的原因,是文法中缺少对文法符号优先级结合性的规定

2.4.2二义性的消除

为文法的符号规定适当的优先级结合性

  • 两种方法
    • 改写二义文法为非二义文法
    • 对二义施加限制,规定优先级和结合性

1) 改写二义文法为非二义文法

  • 栗子:改写二义文法G3.2为非二义文法G3.4
<!--文法G3.4-->
E->E+T|T
T->T*F|F
F->(E)|-F|id

用文法G3.4对id+id*id重新推导
最左推导:

E => E+T => T+T => F+T => id+T => id+T*F 
=> id+F*F => id+id*F => id+id*id

最右推导

E => E+T => E+T*F => F+T*id => E+F*id => E+id*id 
=> T+id*id => F+id*id => id+id*id

在这里插入图片描述
结论

  1. 由于新引入的非终结符限制每一步直接推导均有唯一选择,使得同一句子仅有一颗分析树。
  2. 最终产生的分析树与推导方法无关,而仅与文法的描述有关。
  3. 引入新的非终结符,使得直接推导的步骤数增加,分析树的高度增高,从而分析效率降低
  4. 越接近S的A与a,优先级越低。
  5. 对具有递归定义性质的A产生式A->αAβ,若a∈β(A在a的左边),则a具有左结合性质;若a∈α(A在a的右边),则a具有右结合性质;例如E->E+T中E在+的左边,则+具有左结合性质;若产生式形如E->T+E,则+具有右结合性质。

2)为文法符号规定优先级和结合性
在二义文法G3.2中,只要分别为+、*和-规定正确的优先级和结合性,就会使得分析任何一个句子时仅能得到一颗分析树。

3. 语言与文法

程序设计语言的结构均可以用文法来描述
(1)文法给出了精确的、易于理解的语言结构的说明
(2)以文法为基础的语言,以便于加入新的或修改、删除旧的语言结构
(3)有些类别的文法,可以自动生成高效的分析器

3.1 正规式与上下文无关文法

CFG:所有的产生式左边只有一个非终结符

  1. 正规式到CFG的转换
    推论:正规式所描述的语言结构均可以用CFG来描述,反之不一定
    步骤:
    1. 构造正规式的NFA
    2. 若0为初态,则A0为开始符号
    3. 对于move(i,a)=j,引入产生式Ai->aAj
    4. 对于move(i,ε)=j,引入产生式Ai->Aj
    5. 若i是终态,则引入产生式Ai->ε
      栗子:r=(a|b)*abb的CFG
<!--文法G3.6-->
A0->aA0|bA0|aA1
A1->bA2
A2->bA3
A3->ε

在这里插入图片描述

  1. 为什么用正规式而不用CFG描述程序设计语言的词法
    采用正规式:
    1. 词法规则简单,用正规式描述已足够
    2. 正规式的表示比CFG更直观、简洁,易于理解
    3. 有限自动机的构造比下推自动机简单,且分析效率高
    4. 区分词法和语法,为编译器前端的模块划分提供方便

3.2 上下文有关文法

第一个产生式左边有不止一个符号

所有产生式的左部长度都小于等于右部长度

3.3 形式化语言与自动机

定义:若文法G=(N,T,P,S)的每个产生式α->β中,均有α∈(N∪T)*,且至少含有一个非终结符,β∈(N∪T)*,则称G为0型文法。
对0型文法施加以下第i条限制,即可得到i型文法
(1)G的任何产生式α->β(S->ε除外)均满足|α|<=|β|(|x|表示x中文法符号的个数)
(2)G的任何产生式形如A->β,其中A∈N,β∈(N∪T)*。
(3)G的任何产生式形如A->a,或者A->aB(或者A->Ba),其中A,B∈N,a∈T*。

文法产生式语言自动机
0型(短语)文法α->β0型语言(短语结构语言,递归可枚举集)图灵机
1型文法(CSG)限制11型语言(CSL)线性界线自动机
2型文法(CFG)限制22型语言(CFL)下推自动机
3型(正规)文法限制33型语言(正规语言,正规集)有限自动机

4. 自上而下语法分析

4.1 自上而下分析的一般方法

基本思想:对于任何一个输入序列,从文法的开始符号开始,进行最左推导,直到得到一个合法句子或者发现一个非法结构。在推导过程中试图用一切可能的方法,自上而下、从左到右为输入序列建立分析树。

存在的问题:

  • 左因子:有相同前缀,例如A->αβ1|αβ2
  • 左递归:陷入死循环而使分析无法进行下去,例如A->Aα

4.2 消除左递归

在这里插入图片描述

  1. 消除文法的直接左递归
    对于产生式A->Aα|β,可以用非左递归的A->βA’和A’->αA’|ε取代
    在这里插入图片描述

  2. 消除文法的左递归
    例如:S=>Aa=>Sda
    在这里插入图片描述

4.3 提取左因子

在这里插入图片描述
当一个文法中既有左递归又含左因子时,一般的做法时先消除左递归。

4.4 递归下降分析

  • 写程序
  • 递归下降分析是直接以程序的方式模拟产生式产生语言的过程。
  • 基本思想:为每一个非终结符构造一个子程序,每一个子程序的过程体中按该产生式的候选项分情况展开,遇到终结符匹配,而遇到非终结符就调用相应非终结符的子程序。
    限制:不能有公共左因子和左递归
  • 称为:递归下降子程序
  • 构造过程:
    (1)构造文法的状态转换图并且简化
    (2)将状态图转化为EBNF表示
    (3)从EBNF构造子程序

4.5 预测分析器

预测分析器:又称为非递归预测分析器或者表驱动的预测分析器,数学模型是下推自动机
组成:预测分析表、一个符号栈和一个驱动器

4.5.1 非递归预测分析器的工作模式

  1. 下推自动机与格局
    下推自动机模型
    只读头:ip指向当前输入
    下推栈:top指向栈顶
    有限状态转移控制:
    在这里插入图片描述
    预测分析器:
    下推栈:符号栈,用来存放终结符与非终结符
    有限状态转移控制:预测分析表+驱动器
    在这里插入图片描述
    下推自动机的工作模式:是一种放幻灯的方式,此处的每张“幻灯片”称为一个格局
    格局是一个三元组:(站内容,剩余输入,改变格局的动作)
    分析是从某个初始格局开始的,经过一系列的格局变化,最终到达接受格局,表明分析成功;或者到达出错格局,表明发现一个语法错误。
    开始格局:全部输入序列
    接受格局:剩余输入应该为空
  2. 预测分析表中的内容与改变格局的动作
    四种改变格局的动作:
    1. 匹配终结符:若栈顶和当前输入终结符相等且不是结束标志#,则分析器弹出栈顶符号(pop),输入指针指向下一个终结符(next(ip))。
    2. 展开终结符:栈顶符号是非终结符X,当前输入时终结符a,驱动器访问分析表M[X,a];若M[X,a]时X产生式的某候选项,则用此候选项取代栈顶的X。
    3. 报告分析成功:栈顶和当前输入符号均为#,分析成功并结束。
    4. 报告出错:

栗子:表3.2给出了文法G3.9’的预测分析表。用算法3.4作为驱动器,表3.2作为分析表,分析输入序列id+id*id;的过程如下。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4.5.2 构造预测分析表

  • 计算FIRST集合
    在这里插入图片描述

  • 计算所有非终结符的FOLLOW集合
    在这里插入图片描述
    栗子
    在这里插入图片描述

4.5.3 LL(1)文法

一个文法G被称为时LL(1)文法,当且仅当为它构造的预测分析表中不含有多重定义的条目时。由此分析表所组成的分析器被称为LL(1)分析器,它所分析的语言被称为LL(1)语言

  • 第一个L表示从左到右扫描输入序列
  • 第二个L表示产生最左推导
  • 1表示在确定分析器的每一步动作时间向前看一个终结符

5. 自下而上语法分析

5.1 自下而上分析的基本方法

  1. 规范归约与“剪句柄”
    在这里插入图片描述

在这里插入图片描述

  • 短语:以非终结符为根的子树中所有从左到右排列的叶子
  • 直接短语:只有父子关系的树中所有从左到右排列的叶子(树高为2)
  • 句柄:最左边父子关系树中所有从左到右排列的叶子(句柄是唯一的)
    在这里插入图片描述
  1. 移进——归约分析器的工作模式
    在这里插入图片描述
  • 改变格局的动作:
    • 移进(shift):把当前输入中的下一个终结符移进栈
    • 归约(reduce):句柄在栈顶已形成,用适当产生式左部代替句柄
    • 接受(accept):宣告分析成功
    • 报错(error):发现语法错误,调用语法恢复例程

5.2 LR分析

5.2.1 LR分析与LR文法

LR分析的核心是LR分析表和驱动器
LR分析表由两部分组成:一部分是动作表,另一部分是转移表

  • 栗子
    • 文法
      在这里插入图片描述

    • 移进——归约分析表
      在这里插入图片描述

    • 解释
      s:表示当前状态
      a:表示终结符
      A:表示非终结符
      action[s,a]:指示当前栈顶状态为s和输入终结符为a时应进行的下一动作
      goto[s,A]:指示当前栈顶为s和非终结符A时的下一状态转移

    • 分析过程
      在这里插入图片描述
      在这里插入图片描述

5.2.2 构造SLR(1) 分析器

SLR(1)分析器简称SLR分析器,S表示Simple 简单的意思

  • 构造SLR分析表的基本思想:首先构造一个可以识别文法G中所有活前缀的DFA,然后根据DFA和简单的向前看信息构造SLR分析表
  1. 活前缀与LR(0)项目
    活前缀:出现在移进——归约分析器栈中的右句型的前缀
    LR(0)项目:简称项目,在它右部的某个位置上,右一个点“.”。对于A->ε,它仅有一个项目A->. 。
  2. 拓广文法与识别活前缀的DFA
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    栗子
    在这里插入图片描述
  3. 识别活前缀
    在这里插入图片描述
posted on 2022-06-13 21:26  JAVA开发区  阅读(251)  评论(0)    收藏  举报  来源