国科大_编译原理-个人笔记_前五章

642 阅读10分钟

封面图片选自:Photo by Dominik Van Opdenbosch on Unsplash 文章部分图片节选自国科大编译原理课件

  • 注意:本文含有特殊符号 ε ∅ ∑ ∈ α ⇒

引论

编译程序

  • 编译程序:把某一种高级语言程序等价地转换成另一种低级语言程序(如汇编语言或机器语言程序)的程序

编译过程

图片.png

  • 词法分析:输入源程序,对构成源程序的字符串进行扫描和分解,识别出单词符号
  • 语法分析:在词法分析的基础上,根据语法规则把单词符号串分解成各类语法单位(语法范畴)
  • 中间代码生成:对各类语法单位按语言的语义进行初步翻译(三元式或四元式)
  • 优化
  • 目标代码产生:把中间代码变换成特定机器上的目标代码
    • 汇编指令代码: 需要进行汇编
    • 绝对指令代码: 可直接运行
    • 可重新定位指令代码: 需要连接

编译程序的结构

  • 编译程序总框

图片.png

  • 遍:对源程序或源程序的中间表示从头到尾扫描一次

  • 编译前端与后端

    • 编译前端:与源语言有关,如词法分析,语法分析,语义分析与中间代码产生,与机器无关的优化
    • 编译后端:与目标机有关,与目标机有关的优化,目标代码产生

编译程序的生成

以汇编语言和机器语言为工具

  • 优点: 可以针对具体的机器,充分发挥计算机的系统功能;生成的程序效率高
  • 缺点: 程序难读、难写、易出错、难维护、生产的效率低

高级语言书写

  • 示例一:
    • 已经有1,使用L1语言编写L2语言的编译器的代码2,将该代码使用1编译成L2语言编译器的可执行文件3

图片.png

  • 示例二:
    • 已经有1,代码2使用1生成3,再使用代码4(也就是1)使用3生成5.

图片.png

自编译方式

图片.png

高级程序设计语言概述

下面哪种说法正确? ( )

A.标识符是语义概念,名字是语法概念

B.标识符是语法概念,名字是语义概念

答案B

  • 语法:一组规则,用它可以形成和产生一个合式的程序

    • 词法规则:单词符号的形成规则
    • 语法规则:语法单位的形成规则
  • 语义:一组规则,用它可以定义一个程序的意义

下面哪些属于程序语言的语义定义? ( )

A. 表达式中圆括号必须匹配

B. 类的声明必须以class开头

C. 关于函数调用时参数传递方法的描述

D. 函数体必须用return语句结尾 答案:C

下面说法的是错误的是( )

A.名字的绑定(binding)是指将标识符与所代表的程序数据或代码进行关联

B.名字的绑定总是发生在编译过程中

C.名字的绑定可以发生在运行过程中

答案:B

在C语言中,下面选项只具有右值、不具有左值的是 ( )。

A.变量

B.下标变量

C.a + 5

D.指针变量P

E.*P (P是指针变量)

答案:C

高级程序设计语言的语法描述

基本概念

  • 字母表:一个有穷字符集,记为

  • 字母表中每个元素称为字符

  • ∑上的(也叫字符串) 是指由∑中的字符所构成的一个有穷序列

  • 不包含任何字符的序列称为空字,记为ε

  • 表示∑上的所有字的全体,包含空字ε。例如: 设 ∑={a, b},则 ∑*={ε,a,b,aa,ab,ba,bb,aaa,...}

  • ∑*的子集U和V的连接(积)定义为图片.png

  • 图片.png

上下文无关文法

  • 上下文无关文法G是一个四元组:G=(VT,VN,S,P),其中:

    • VT:终结符(Terminal)集合(非空)
    • VN:非终结符(Noterminal)集合(非空),且VT与VN不能重复
    • S:文法的开始符号,S属于VN
    • P:产生式集合(有限),每个产生式形式为
    • 开始符S至少必须在某个产生式的左部出现一次
    • 图片.png
  • 候选式的缩写

图片.png

文法生成语言

  • 直接推导

图片.png

  • 图片.png

从文法到语言/语言到文法

图片.png

图片.png

图片.png

图片.png

推导与语法树

  • 从一个句型到另一个句型的推导往往不唯一
  • 最左推导:任何一步推导都是对式子中的最左非终结符进行替换
  • 最右推导:任何一步推导都是对式子中的最右非终结符进行替换
  • 语法树

图片.png

语法树与二义性

  • 一个句型不一定只对应唯一一棵语法树
  • 文法的二义性:如果一个文法存在某个句子对应两棵不同的语法树,则说这个文法是二义的

形式语言鸟瞰

图片.png

图片.png

词法分析器的设计--状态转换图

词法分析概述

  • 词法分析的任务:从左至右逐个字符地对源程序进行扫描,产生一个个单词符号

  • 词法分析器:执行词法分析的程序

  • 输出的单词符号的表示形式:(单词种别,单词自身的值)

图片.png

  • 词法分析作为一个独立的阶段,但不一定不作为单独的一遍

词法分析器的结构

图片.png

  • 缓存长度

图片.png

超前搜索

  • 例如:DO99K=1, 10DO99K=1.10。第一个是DO 99 K = 1,10,第二个是DO99K是一个变量名。为了识别是DO还是DO99K,词法分析器需要超前搜索,第一个搜索到,才知道是循环结构,第二个搜索到.才能识别变量DO99K。这种超前搜索识别后还需要回退,费时间。
  • 避免超前搜索的几点限制:
    • 所有基本字都是保留字;用户不能用它们作自己的标识符
    • 基本字作为特殊的标识符来处理,使用保留字表
    • 如果基本字、标识符和常数(或标号)之间没有确定的运算符或界符作间隔,则必须使用一个空白符作间隔

状态转换图

  • 状态转换图是一张有限方向图

    • 结点代表状态,用圆圈表示
    • 状态之间用箭弧连结,箭弧上的标记(字符)代表射出结状态下可能出现的输入字符字符类
    • 一张转换图只包含有限个状态,其中有一个为初态,至少要有一个终态
    • 终态用两个同心圆标识。如果遇到终态识别后需要回退(重新读取刚刚的符号),需要加上星号*
      • 图片.png
  • 状态转换图可用于识别(或接受)一定的字符串

    • 若存在一条从初态到某一终态的道路,且这条路上所有弧上的标记符连接成的字(字符串)等于,则称被该状态转换图所识别(接受)

图片.png

  • 词法分析器的设计示例
    • 单词表图片.png
    • 设计状态转换图图片.png

状态转换图的程序实现

  • 不含回路的分叉结点
    • 可用一个CASE语句或一组IF-THEN-ELSE语句实现

图片.png

GetChar( );
if (IsLetter( )) 
	{…状态j的对应程序段…;}
else if (IsDigit( ))
	{…状态k的对应程序段…;}
else if (ch=‘/’) 
	{…状态m的对应程序段…;}
else 
	{…错误处理…;}
  • 含回路的状态结点
    • 对应一段由WHILE结构和IF语句构成的程序

图片.png

GetChar( );
while (IsLetter( )or IsDigit( ))
	  GetChar( );
…状态j的对应程序段…
  • 终态结点
    • 表示识别出某种单词符号,对应返回语句

图片.png

RETURN (C,VAL)
/* 其中,C为单词种别,VAL为单词自身值 /*
  • 全局变量与过程

    • ch 字符变量,存放最新读入的源程序字符
    • strToken 字符数组,存放构成单词符号的字符串
    • GetChar 子程序过程,把下一个字符读入到 ch 中
    • GetBC 子程序过程,跳过空白符,直至 ch 中读入一非空白符
    • Concat 子程序,把ch中的字符连接到 strToken
    • IsLetterIsDisgital 布尔函数,判断ch中字符是否为字母和数字
    • Reserve 整型函数,对于 strToken 中的字符串查找保留字表,若它是保留字则给出它的编码,否则回送0
    • Retract 子程序,把搜索指针回调一个字符位置
    • InsertId 整型函数,将strToken中的标识符插入符号表,返回符号表指针
    • InsertConst 整型函数过程,将strToken中的常数插入常数表,返回常数表指针
  • 例子:

图片.png

int code, value;
strToken := “ ”;	/*置strToken为空串*/
GetChar(); GetBC();
if (IsLetter())
begin
	while (IsLetter() or IsDigit())
	begin
		Concat(); GetChar(); 
	end
	Retract();/*注意回退*/
	code := Reserve();
	if (code = 0)
	begin
		value := InsertId(strToken);
		return ($ID, value);
	end
	else
		return (code, -);	
end

图片.png

else if (IsDigit())
begin
	while (IsDigit())
	begin
		Concat( ); GetChar( );
	end
	Retract();
	value := InsertConst(strToken);
	return($INT, value);
end
else if (ch =‘=’) return ($ASSIGN, -);
else if (ch =‘+’) return ($PLUS, -);

图片.png

else if (ch =‘*’)
begin
	GetChar();
	if (ch =‘*’) return ($POWER, -);
	Retract(); return ($STAR, -);
end
else if (ch =‘,’) return ($COMMA, -);
else if (ch =‘(’) return ($LPAR, -);
else if (ch =‘)’) return ($RPAR, -);
else ProcError( );		/* 错误处理*/
  • 将状态图的代码一般化
    • 变量curState用于保存现有的状态
    • 用二维数组表示状态图:stateTrans[state][ch]
curState = 初态
GetChar();
while( stateTrans[curState][ch]有定义){
   //存在后继状态,读入、拼接
   Concat();
   //转换入下一状态,读入下一字符   curState= stateTrans[curState][ch];
   if curState是终态 then 返回strToken中的单词
   GetChar( ); 
}

词法规则的形式化

正规集和正规式

  • 正规集和正规式
    • 正规集可以用正规式表示
    • 正规式是表示正规集一种方法
    • 一个字集合是正规集当且仅当它能用正规式表示

正规式和正规集的递归定义

  • 对给定的字母表∑,仅由有限次使用上述三步骤而定义的表达式才是∑上的正规式,仅由这些正规式表示的字集才是∑上的正规集。
  1. ε和∅都是∑上的正规式,它们所表示的正规集为{ε}和∅;
  2. 任何a∈∑,a是∑上的正规式,它所表示的正规集为{a} ;
  3. 假定e1和e2都是∑上的正规式,它们所表示的正规集为L(e1)和L(e2),则
    1. (e1|e2)为正规式,它所表示的正规集为L(e1)∪L(e2)
    2. (e1.e2)为正规式,它所表示的正规集为L(e1)L(e2)
    3. (e1)*为正规式,它所表示的正规集为(L(e1))*

ε是什么?

A. 字符

B. 字

C. 正规式

答案:BC

∅是什么?

A. 集合

B. 字

C. 正规式

答案:AC

a(a∈∑)是什么?

A.字符

B. 字

C. 正规式

答案:ABC

正规式的等价

  • 若两个正规式所表示的正规集相同,则称这两个正规式等价。

图片.png

  • 正规式的性质

图片.png

确定有限自动机(DFA)

  • 定义:

图片.png

  • DFA表示为状态转换图

    • 假定DFA M含有m个状态和n个输入字符
    • 对应的状态转换图含有m个状态结点,每个结点顶多含有n条箭弧射出,且每条箭弧用Σ上的不同的输入字符来作标记
  • 对于∑*中的任何字α,若存在一条从初态到某一终态的道路,且这条路上所有弧上的标记符连接成的字等于α,则称α为DFA M所识别(接收)。DFA M所识别的字的全体记为L(M)

图中DFA M识别的L(M) 是什么?

图片.png

A. L(M)={以aa或bb开头的字}

B. L(M)={含aa或bb的字}

C. L(M)={以aa或bb结尾的字}

答案:B

哪个DFA识别{ε} ?

图片.png

答案:A

  • DFA的程序实现

图片.png

非确定有限自动机(NFA)

  • 定义:

图片.png

  • 从状态图看NFA 和DFA的区别
    • NFA可以有多个初态
    • 弧上的标记可以是∑*中的一个(甚至可以是一个正规式),而不一定是单个字符
    • 同一个字可能出现在同状态射出的多条弧上

图片.png

  • 对于∑*中的任何字α,若存在一条从初态到某一终态的道路,且这条路上所有弧上的标记字连接成的字等于α(忽略那些标记为ε的弧),则称α为NFA M所识别(接收)
    • NFA M所识别的字的全体记为L(M)

图中DFA M识别的L(M) 是什么?

图片.png

A. L(M)={以aa或bb开头的字}

B. L(M)={含aa或bb的字}

C. L(M)={以aa或bb结尾的字}

答案:B

图中NFA M2识别的L(M2) 是什么?

图片.png

A. L(M2)={abnab^n | n≥1}

B. L(M2)={anbna^nb^n | n≥1}

C. L(M2)={ambna^mb^n | m,n≥1}

答案:C

  • DFA与NFA
    • 对于任何两个有限自动机M和M’,如果L(M)=L(M’),则称M与M’等价
    • 对于每个NFA M存在一个DFA M’,使得 L(M)=L(M’)

有限自动机的等价性——NFA转换成DFA

等价性证明

  1. 图片.png

图片.png

  1. 对M的状态转换图进一步施行替换,其中k是新引入的状态。 图片.png

图片.png

  1. 图片.png

NFA确定化

  • 子集法
  • 图片.png
  • 图片.png

例子:

图片.png

  • 确定化:不失一般性,设字母表只包含两个字符a和b,我们构造一张计算状态集的转换表: 图片.png
    • 首先,置第1行第1列为ε-closure({X})求出这一列的Ia,Ib;
    • 然后,检查这两个Ia,Ib,看它们是否已在表中的第一列中出现,把未曾出现的填入后面的空行的第1列上,求出每行第2,3列上的集合...
    • 重复上述过程,直到所有第2,3列子集全部出现在第一列为止

图片.png

  • 转换表唯一刻划了一个确定的有限自动机M
    • 初态ε-closure({X})
    • 终态是含有原终态Y的子集

图片.png

  • 不难看出,这个DFA M与M’等价

DFA的化简

  • DFA的化简(最小化)

    • 对于给定的DFA M,寻找一个状态数比M少的DFA M’,使得L(M)=L(M’)
  • 状态的等价性:假设s和t为M的两个状态,称s和t等价:如果从状态s出发能读出某个字α而停止于终态,那么同样,从t出发也能读出α而停止于终态;反之亦然

    • 两个状态不等价,则称它们是可区别的

两个状态s和t是可区分的,是指( )

A. 对于任意字α,要么s读出α停止于终态而t读出α停止于非终态,要么t读出α停止于终态而s读出α停止于非终态

B. 存在一个字α,要么s读出α停止于终态而t读出α停止于非终态,要么t读出α停止于终态而s读出α停止于非终态

答案:B

  • 基本思想
    • 把M的状态集划分为一些不相交的子集,使得任何两个不同子集的状态是可区别的,而同一子集的任何两个状态是等价的。
    • 最后,让每个子集选出一个代表,同时消去其他状态。

按照上述原则对DFA的状态集合S进行第一次划分,正确的分法是( )

A. 初态和非初态

B. 终态和非终态

C. 初态、终态、其他状态

答案:B

  • 例子:找出终态与非终态,然后不断划分,直到划不动为止。从每一个子集中选一个状态代表这个子集,原来子集中所有射入射出的线归为这个状态所有。

图片.png

正规式与有限自动机的等价性

为NFA构造正规式

  1. 图片.png

图片.png

  1. 然后,反复使用下面的三条规则,逐步消去结点,直到只剩下X和Y为止。

图片.png

复杂的消去

图片.png

  1. 最后,X到Y的弧上标记的正规式即为所构造的正规式r。显然L(r)=L(M’)=L(M)

图片.png

为正规式构造NFA

  • 定理: 对于∑上的正规式r,都存在一个NFA M,使L(M)=L(r),并且M只有一个初态和一个终态,而且没有从终态出发的箭弧
  • 证明:归纳法
  1. 首先,把正规式r表示成:图片.png
  2. 按下面的三条规则对r进行分裂

图片.png

  1. 逐步把这个图转变为每条弧只标记为∑上的一个字符或ε,最后得到一个NFA M’,显然L(M’)=L(r)
    • 转换完后,还可以把这个NFA进一步转化为DFA并化简。 图片.png

语法分析基本概念

  • 对语言的语法结构进行描述

    • 采用正规式有限自动机描述和识别语言的单词符号
    • 上下文无关文法来描述语法规则
  • 语法分析的任务

    • 分析一个文法的句子的结构
  • 语法分析器的功能

    • 按照文法的产生式(语言的语法规则),识别输入符号串是否为一个句子(合式程序)
  • 语法分析器的结构

图片.png

  • 语法分析的方法
自下而上(Bottom-up)自上而下(Top-down)
从输入串开始,逐步进行归约,直到文法的开始符号从文法的开始符号出发,反复使用各种产生式,寻找"匹配"的推导
归约:根据文法的产生式规则,把串中出现的产生式的右部替换成左部符号推导:根据文法的产生式规则,把串中出现的产生式的左部符号替换成右部
从树叶节点开始,构造语法树从树的根开始,构造语法树
算符优先分析法、LR分析法递归下降分析法、预测分析程序

自上而下分析

  • 基本思想
    • 从文法的开始符号出发,向下推导,推出句子
    • 针对输入串,试图用一切可能的办法,从文法开始符号(根结点)出发,自上而下地为输入串建立一棵语法树

图片.png

文法左递归问题

  • 一个文法是含有左递归的,如果存在非终结符P,如果有产生式P→Pa|b,那么程序可能一直推导P→Pa,一直递归下去……

图片.png

消除文法的左递归

  • 图片.png

图片.png

图片.png

  • 一个文法消除左递归的条件

    • 不含以ε为右部的产生式
    • 不含回路图片.png
  • 间接左递归的消除

图片.png

  • 消除左递归的算法

图片.png

回溯问题

  • 分析过程中,当一个非终结符用某一个候选匹配成功时,这种匹配可能是暂时的。出错时,不得不“回溯

图片.png 例如,上图中,有推导:A→**|*。但是输入串是x*y。当符号指针指向表达式中的*时,根据推导式,会首先匹配A→**,但显然当IP(符号指针)指向y时,就与推导式中的**不匹配。因此就得回溯,转而匹配A→*

消除回溯

  • 对文法的任何非终结符,当要它去匹配输入串时,能够根据它所面临的输入符号准确地指派它的一个候选去执行任务,并且此候选的工作结果应是确信无疑的,不用选了个错的候选又得回来。

FIRST集合

  • 定义:

图片.png

图片.png

  • 为了满足所有候选首符集两两不相交,需要提取公共左因子:

图片.png

  • 构造FIRST集合:

图片.png

构造任何符号串的FIRST集合 图片.png

FOLLOW集合

  • 有的产生式会产生ε。有时候,我们需要匹配ε来选择其他的产生式的候选(如下图)。那么,什么时候需要匹配呢?引入FOLLOW集合。 图片.png

  • 定义: 图片.png

  • 构造每个非终结符的FOLLOW集合 图片.png

LL(1)文法

  • 定义: 图片.png

  • 分析符号串 图片.png

递归下降分析程序

  • 分析程序由一组子程序组成, 对每一语法单位(非终结符)构造一个相应的子程序,识别对应的语法单位

  • 定义全局过程和变量

    • ADVANCE,把输入串指示器IP指向下一个输入符号,即读入一个单词符号
    • SYM,IP当前所指的输入符号
    • ERROR,出错处理子程序
  • A→TE'|BC|ε ,对应的递归下降子程序为:

PROCEDURE A;
BEGIN
    IF  SYM∈FIRST(TE’) THEN
        BEGIN T;E	END
    ELSE IF  SYM∈FIRST(BC) THEN
        BEGIN B; C	END
    ELSE IF  SYM∈FOLLOW(A) THEN
        BEGIN  		END
    ELSE ERROR
END;

案例

图片.png

PROCEDURE  F;
	IF SYM=‘i’ THEN ADVANCE
	ELSE
		IF SYM=‘(’ THEN
		BEGIN
			ADVANCE;
			E;
			IF SYM=‘)’ THEN ADVANCE
				ELSE ERROR
		END
		ELSE ERROR;
PROCEDURE  E;
BEGIN	
           T;E’
END
PROCEDURE  E';
IF SYM=‘+’ THEN	    
BEGIN	
	ADVANCE;
          T;E'	
END
ELSE  IF SYM<>‘#’ AND SYM<>’)’ 
                 THEN ERROR
PROCEDURE  T;
BEGIN
	   FT'
END
PROCEDURE  T';
IF SYM=‘*’ THEN
BEGIN
           ADVANCE;
           F;T'
END
ELSE  IF SYM<>‘#’ AND SYM<>’)’ AND SYM<>’+’ 
           THEN  ERROR

主程序:

PROGRAM PARSER;
BEGIN
    ADVANCE;
    E;
    IF SYM <>’#’ THEN 
        ERROR
END;

思考:如果把E'()改写成如下形式:

PROCEDURE  E';
IF SYM=‘+’ THEN
BEGIN	
	ADVANCE;
          T;E'	
END

E'实现了ε吗 ?()

A. 实现了

B. 没实现

答案:A

遇到ε也就是什么也不匹配,进而转到其他程序继续匹配。

上面的代码中,没有考虑Follow集合,也没有ERROR出错处理语句。E'不考虑Follow集合有问题吗 ?()

A. 有问题

B. 没有问题

答案:B 在这里E'结束后会跳到其他函数。如果输入串有错误,在其他函数里照样会检测出来。

扩充的巴科斯范式和语法图

  • 扩充的巴科斯范式:

图片.png

例如,通常的“实数”可定义为:

Decimal→[Sign]Integer.{digit}[Exponent]
Exponent→E[Sign]Integer
Integer→digit{digit}
Sign→ + | -

图片.png

预测分析程序

原理

  • 总控程序,根据现行栈顶符号和当前输入符号,执行动作
  • 分析表 M[A,a]矩阵,A ∈ VNV_N ,a ∈ VTV_T 是终结符或‘#’
  • 分析栈 STACK 用于存放文法符号

图片.png

  1. 若X=a=‘#’,则宣布分析成功,停止分析。
  2. 若X=a ≠‘#’,则把X从STACK栈顶逐出,让a指向下一个输入符号。
  3. 若X是一个非终结符,则查看分析表M。
    • 若M[X,a]中存放着关于X的一个产生式,把X逐出STACK栈顶,把产生式的右部符号串按反序依次推进STACK栈(产生式右部箭头的第一个符号放栈顶)(若右部符号为ε ,则意味不推什么东西进栈)。
    • 若M[X,a]中存放着“出错标志”,则调用出错诊察程序ERROR。
  • 程序实现
BEGIN
    首先把‘#’然后把文法开始符号推进STACK栈;
    把第一个输入符号读进a;
    FLAG:=TRUE;
    WHILE  FLAG  DO
    BEGIN
            把STACK栈顶符号上托出去并放在X中;
            IF X∈VT THEN
                    IF X= a
                            THEN 把下一输入符号读进a
                            ELSE   ERROR
            ELSE IF X=‘#’ THEN
            IF X=a
                    THEN FLAG:=FALSE 
                            ELSE ERROR
            ELSE IF M[X,a]={X→X1X2…Xk}THEN
                    把Xk,Xk-1,…,X1一一推进STACK栈
                    /* 若X1X2…Xk=ε,不推什么进栈 */
            ELSE ERROR
    END OF WHILE;
STOP /*分析成功,过程完毕*/
END

构造预测分析表

  • 构造G的分析表M[A,a], 确定每个产生式A→α在表中的位置
  1. 对文法G的每个产生式A→α执行第2步和第3步;
  2. 对每个终结符a∈FIRST(α),把A→α加至M[A,a]中;
  3. 若ε∈FIRST(α),则对任何b∈FOLLOW(A)把A→α加至M[A,b]中。
  4. 把所有无定义的M[A,a]标上“出错标志”。

综合案例: 图片.png

LL(1)文法与二义性

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

实际上,程序语言中,有许多文法是二义的。例如:

G(S):
	SiCtS | iCtSeS | a
	Cb

这是一个if … then ... else …的文法。i代表if,t代表then,e代表else。

提取左因子之后,改写成:

G(S):
	SiCtSS’ | a
	S’→ eS | ε
	Cb

构造预测分析表如下: 可以看到,在[S',e]中,有两个产生式。如果交给程序,显然这是不合理的。实际上,在程序中会遇到如下的是的语句:

if ... then  if ... then  ... else

这里可以解释为else与就近的then匹配。

if ... 
then  
	if ... 
	then ... 
	else ...

或者与远端的then匹配:

if ... 
then  
	if ... 
	then  ... 
else ...

大多数程序会选择else与就近的then匹配。在实际中,编译程序会将预测分析表中冲突的地方手动指定要选择的产生式。

自下而上分析

  • 采用“移进-归约”思想进行自下而上分析
    • 用一个寄存符号的先进后出栈,把输入符号一个一个地移进到栈里,当栈顶形成某个产生式的候选式时,即把栈顶的这一部分替换成(归约为)该产生式的左部符号。

图片.png

你认为什么是可归约串?

A. 连续出现的单词序列

B. 短语

答案:B

  • 短语: 图片.png

  • 直接短语: 图片.png

图片.png

  • 小技巧:在一个句型对应的语法树中

    • 以某非终结符为根的两代以上的子树的所有末端结点从左到右排列就是相对于该非终结符的一个短语
    • 如果子树只有两代,则该短语就是直接短语
  • 分析过程描述 图片.png

算符优先分析方法

  • 图片.png

  • 一个文法,如果它的任一产生式的右部都不含两个相继(并列)的非终结符,即不含…QR…形式的产生式右部,则我们称该文法为算符文法

    • 约定:
      • a、b代表任意终结符
      • P、Q、R代表任意非终结符
      • ‘…’代表由终结符和非终结符组成的任意序列,包括空字
  • 图片.png

图片.png

FIRSTVT和LASTVT集合

图片.png

  • 根据FIRSTVT和LASTVT集合,检查每个产生式的候选式,确定满足关系<>的所有终结符对
    • 假定有个产生式的一个候选形为…aP…, 那么,对任何b∈FIRSTVT(P),有 a<b
    • 假定有个产生式的一个候选形为…Pb…, 那么,对任何a∈LASTVT(P),有 a>b

构造集合FIRSTVT(P)、LASTVT(P)的算法

  • 反复使用下面两条规则构造集合FIRSTVT(P)

    1. 若有产生式P→a…P→Qa…,则a∈FIRSTVT(P)
    2. a∈FIRSTVT(Q),且有产生式P→Q…,则a∈FIRSTVT(P)
  • 反复使用下面两条规则构造集合LASTVT(P)

    1. 若有产生式P→…aP→…aQ,则 a∈LASTVT(P)
    2. a∈LASTVT(Q),且有产生式P→…Q,则a∈LASTVT(P)

图片.png

  • 算法的一种实现
    • 布尔数组F[P,a],使得F[P,a]为真的条件是,当且仅当a∈FIRSTVT(P)。开始时,按上述的规则1对每个数组元素F[P,a]赋初值。
    • 栈STACK,把所有初值为真的数组元素F[P,a]的符号对(P,a)全都放在STACK之中。
    • 若栈STACK不空,就将栈顶项弹出,记此项为(Q,a)。对于每个形如P→Q…的产生式,若F[P,a]为假,则变其值为真且将(P,a)推进STACK栈。
    • 上述过程一直重复,直至栈STACK为空为止。
PROCEDURE  INSERT(P,a)
IF  NOT  F[P,a]  THEN
BEGIN 
      F[P,a]:=TRUE;
     把(P,a)下推进STACK栈 
END
  • 主程序:
BEGIN
FOR  每个非终结符P和终结符a  DO 
	F[P,a]:=FALSEFOR  每个形如P→a…或P→Qa…的产生式  DO
	INSERT(P,a);
WHILE STACK 非空 DO
BEGIN
	把STACK的顶项,记为(Q,a),上托出去;
	FOR 每条形如P→Q…的产生式   DO
		INSERT(P,a);
END OF WHILEEND

构造优先关系表

  • 构造优先关系表的算法 图片.png
FOR  每条产生式PX1X2Xn  DO
    /*P → X1 X2 … Xi  Xi+1 Xi+2 … Xn*/
    FOR  i:=1  TO  n-1 DO
        BEGIN
            IF  XiXi+1均为终结符  THENXi=Xi+1
            IF  i<=n-2XiXi+2都为终结符, 但Xi+1为非终结符  THENXi=Xi+2IF  Xi为终结符而Xi+1为非终结符  THEN
                FOR  FIRSTVT(Xi+1)中的每个a  DOXi<aIF  Xi为非终结符而Xi+1为终结符  THEN
                    FOR  LASTVT(Xi)中的每个a   DOa>Xi+1
		END

图片.png

最左素短语

  • 一个文法G的句型的素短语是指这样一个短语,它至少含有一个终结符,并且,除它自身之外不再含任何更小的素短语
  • 最左素短语是指处于句型最左边的那个素短语

图片.png

最左素短语定理

图片.png

理解:

图片.png

算符优先分析算法的实现

  • 使用一个符号栈S,用它寄存终结符和非终结符,k代表符号栈S的使用深度
  • 在正确的情况下,算法工作完毕时,符号栈S应呈现:# N #

图片.png

对于文法的句子来说,它的算符优先分析的结果就是语法树

A. 正确

B. 错误

答案:B

图片.png

算符优先分析程序构成

图片.png

LR分析法

  • LR分析法

    • L:从左到右扫描输入串
    • R:自下而上进行归约
  • 工作框架

图片.png

句柄和规范归约

  • 句柄:

图片.png

  • 在一个句型对应的语法树中,最左两代子树末端就是句柄

Video_2023-02-19_113915.gif

  • 规范归约 图片.png

    • 规范归约是最左归约
    • 规范归约的逆过程就是最右推导S ⇒ aAcBe ⇒ aAcde ⇒ aAbcde ⇒ abbcde
    • 最右推导也称为规范推导
    • 由规范推导推出的句型称为规范句型

LR分析器

  • LR分析器的结构
    • LR分析方法:把"历史"及"展望"综合抽象成状态;由栈顶的状态现行的输入符号唯一确定每一步工作

图片.png

LR分析表

  • ACTION[s,a]:当状态s面临输入符号a时,应采取什么动作.
  • GOTO[s,X]:状态s面对文法符号X时,下一状态是什么

图片.png

LR分析过程

  • 分析开始时: (s0s_0,#,a1a2...ana_1a_2 ... a_n #)

图片.png

图片.png

图片.png

所生成的归约得到的树如下:

图片.png

LR文法

  • 定义:对于一个文法,如果能够构造一张分析表,使得它的每个入口均是唯一确定的,则这个文法就称为LR文法
  • 定义:一个文法,如果能用一个每步顶多向前检查k个输入符号的LR分析器进行分析,则这个文法就称为LR(k)文法.

LR分析器的性质

  • 栈内的符号串和扫描剩下的输入符号串构成了一个规范句型一旦栈的顶部出现可归约串(句柄),则进行归约

对于句子,在规范归约过程中,栈内的符号串和扫描剩下的输入符号串构成了一个规范句型,下面哪种格局不会出现:

图片.png

答案:D。如果句柄出现在栈顶,分析器会立即进行规约,不会让它进入栈内。

构造LR分析表

活前缀

  • 字的前缀:是指字的任意首部,如字abc的前缀有 ε,a,ab,abc
  • 活前缀:是指规范句型的一个前缀,这种前缀不含句柄之后的任何符号。即,对于规范句型αβγ,β为句柄,如果αβ=u1u2…ur,则符号串u1u2…ui(1<=i<=r)是αβγ的活前缀。(γ必为终结符串)
  • 规范归约过程中,保证分析栈中总是活前缀,就说明分析采取的移进/归约动作是正确的。(也就是栈顶总会有句柄可以归约)

图片.png

构造识别活前缀的DFA

  • 将文法G(S)拓广为G‘(S‘)

    • 构造文法G‘,它包含了整个G,并引进不出现在G中的非终结符S‘、以及产生式S‘→S,S‘是G‘的开始符号
    • 称G‘是G的拓广文法
  • LR(0)项目:在每个产生式的右部添加一个圆点,表示我们在分析过程中看到了产生式多大部分

  • A→XYZ有四个项目:A→•XYZ(代表此时即将读入XYZ)、A→X•YZ(代表已经读入X,即将读入YZ。X已经进栈)、A→XY•ZA→XYZ•(代表XYZ已经读入。都已经进栈)

  • 构造识别文法所有活前缀的DFA

图片.png

  • 构成识别一个文法活前缀的DFA的项目集(状态)的全体称为文法的LR(0)项目集规范族

2023-02-20_172839.gif

构造LR(0)分析表

  • LR(0)文法:假若一个文法G的拓广文法G'的活前缀识别自动机中的每个状态(项目集)不存在下述情况:

    • 既含移进项目又含归约项目;
    • 含有多个归约项目
  • LR(0)分析表的构造

图片.png

基本完成 2023-02-28