暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

北航编译原理课件

yBmZlQzJ 2024-06-05
146

编译的起源:程序设计语言的发展

基本概念

编译过程和编译程序构造

编译技术的应用

北京航空航天大学计算机学院

1

机器语言

(机器指令)

面向用户

的语言

面向问题

的语言

汇编语言

低级语言

高级语言

北京航空航天大学计算机学院

2

低级语言(Low level Language)

字位码、机器语言、汇编语言

特点:与特定的机器有关,效率高、灵活,但使用复

杂、繁琐、编写费时、易出错

高级语言

-- Fortran、Pascal、C 语言等

–特点:不依赖具体机器,移植性好、便于描述问题处

理过程和算法、易使用、易维护等。

用高级语言编制的程序,计算机不能立即执行,

必须通过一个“翻译程序”加工,转化为与其等价的

机器语言程序,机器才能执行。

这种翻译程序,称之为“编译程序”。

北京航空航天大学计算机学院

3

源程序

用汇编语言或高级语言编写的程序称为源程序。

目标程序

用目标语言所表示的程序。

目标语言:可以是某种机器的机器语言或汇编语言,

也可以是介于源语言和机器语言之间的“中间语言”,

甚至可以是另一种高级语言。

翻译程序

将源程序转换为目标程序的程序称为翻译程序。

它是指各种语言的翻译器,包括汇编程序和编译程序,

是汇编程序、编译程序以及各种变换程序的总称。

北京航空航天大学计算机学院

4

源程序、翻译程序、目标程序三者关系:

源程序

翻译程序

目标程序

OBJECT

PROGRAM

SOURCE

PROGRAM

TRANSLATER

即源程序是翻译程序的输入,目标程序是翻译程序的输出

北京航空航天大学计算机学院

5

汇编程序

若源程序用汇编语言书写,经过翻译程序得到用机器语言

表示的程序,这时的翻译程序就称之为汇编程序,这种翻译过

程称为“汇编”(Assemble)

编译程序

若源程序是用高级语言书写,经加工后得到目标程序,

这种翻译过程称“编译”(Compile)

汇编程序与编译程序都是翻译程序,主要区别是加工对象的

不同。

北京航空航天大学计算机学院

6

编译或汇编阶段

编译程序

或汇编程序

源程序

目标程序

输出数据

运行阶段

目标程序

+

运行子程序

输入数据

北京航空航天大学计算机学院

7

解释程序(Interpreter)

对源程序进行解释执行的程序。

工作过程

源程序

输入数据

输出数据

解释程序

北京航空航天大学计算机学院

8

源程序

编译程序

源程序的中间形式

输入数据

输出数据

解释程序

北京航空航天大学计算机学院

9

编译过程是指将高级语言程序翻译为等价的目标程

序的过程。

习惯上是将编译过程划分为5个基本阶段:

词法分析

语法分析

语义分析、生成中间代码

代码优化

生成目标程序

北京航空航天大学计算机学院

1

0

一、词法分析

任务:分析和识别单词。

源程序是由字符序列构成的,词法分析扫描源程序

(字符串),根据语言的词法规则分析并识别单词,并以某

种编码形式输出 。

•单词:是语言的基本语法单位,一般语言有四大类单词

<

<

<

<

1>语言定义的关键字或保留字(如BEGIN、END、IF)

2>标识符

3>常数

4>分界符(运算符) (如+、-、*、/、;、(、) ……)

北京航空航天大学计算机学院

11

二、语法分析

任务:根据语法规则(即语言的文法),分析并识别出

各种语法成分,如表达式、各种说明、各种语句、

过程、函数等,并进行语法正确性检查。

X1:= ( 2.0 + 0.8 ) * C1

赋值语句的文法 :

<

<

<

<

赋值语句>→<变量><赋值操作符><表达式>

变量>→<简单标识符>

赋值操作符>→:=

表达式>→ ……

北京航空航天大学计算机学院

1

2

三、语义分析、生成中间代码

任务:对识别出的各种语法成分进行语义分析,

并产生相应的中间代码。

中间代码:一种介于源语言和目标语言之间的中间语言形式

生成中间代码的目的:

<

<

1> 便于做优化处理;

2> 便于编译程序的移植。

中间代码的形式:编译程序设计者可以自己设计,常用的有

四元式、三元式、逆波兰表示等。

北京航空航天大学计算机学院

1

3

四元式(三地址指令)

X1:= ( 2.0 + 0.8 ) * C1

运算符 左运算对象 右运算对象 结果

(1) +

(2) *

(3) :=

2.0

T1

X1

0.8

C1

T2

T1

T2

其中T1和T2为编译程序引入的工作单元

四元式的语义为: 2.0 + 0.8 → T1

T1 * C1 → T2

T2 → X1

这样所生成的四元式与原来的赋值语句在语言的

形式上不同,但语义上等价。

北京航空航天大学计算机学院

1

4

四、代码优化

目的: 是为了得到高质量的目标程序。

例如:前面的四元式中第一个四元式是计算常

量表达式值,该值在编译时就可以算出并存放在工

作单元中,不必生成目标指令来计算,这样四元式

可优化为:

编译时: 2.0 + 0.8 → T1

1) * T1 C1 T2

2) := X1 T2

北京航空航天大学计算机学院

1

5

五、生成目标程序

由中间代码很容易生成目标程序(地址指令序列)。这

部分工作与机器关系密切 ,所以要根据机器进行。在做这

部分工作时(要注意充分利用累加器),也可以进行优化

处理。

X1:= ( 2.0 + 0.8 ) * C1

注意:在翻译成目标程序的过程中,要切记保持

语义的等价性。

北京航空航天大学计算机学院

1

6

1.3.2 编译程序构造

一、 编译程序的逻辑结构

按逻辑功能不同,可将编译过程划分为五个基本阶段,

与此相对应,我们将实现整个编译过程的编译程序划分为

五个逻辑阶段(即五个逻辑子过程)。

语义分析

生成中间

代码

词法分析 语法分析

程序 程序

代码优化 生成目标

程序 程序

S.P

O.P

北京航空航天大学计算机学院

1

7

在上列五个阶段中都要做两件事:

(1)建表和查表;(2)出错处理;

所以编译程序中都要包括符号表管理和出错处理两部分

符号表管理

在整个编译过程中始终都要贯穿着建表(填表)和查表的工

作。即要及时地把源程序中的信息和编译过程中所产生的信息登

记在表格中,而在随后的编译过程中同时又要不断地查找这些表

格中的信息。

出错处理

规模较大的源程序难免有多种错误,编译程序必须要有出错

处理的功能。即能诊察出错误,并能报告用户错误的性质和位

置,以便用户修改源程序。出错处理能力的大小是衡量编译程

序质量好坏的一个重要指标。

北京航空航天大学计算机学院

1

8

典型的编译程序具有7个逻辑部分

S.P

词法分析程序

语法分析程序

语义分析、生成中间代码

代码优化程序

生成目标程序

O.P

北京航空航天大学计算机学院

1

9

二、 遍(PASS)

遍:对源程序(包括源程序中间形式)从头到尾扫描一次,

并做有关的加工处理 ,生成新的源程序中间形式或目标程序,

通常称之为一遍。

第一遍

第二遍

S.P

中间形式1

S.P

中间形式2

S.P

C1

C2

O.P

一遍

要注意遍与基本阶段的区别

五个基本阶段:是将源程序翻译为目标程序在逻辑上要完成的

工作。

遍:是指完成上述5个基本阶段的工作,要经过几次扫描处理。

北京航空航天大学计算机学院

2

0

一遍扫描即可完成整个编译工作的称为一遍扫描编译程序

其结构为:

语法成分

语义分析生成

目标程序

语法分析

S.P.

返回分析结果

取单词

返回单词

词法分析

整理目标程序

停机

O.P.

北京航空航天大学计算机学院

2

1

三、前端和后端

根据编译程序各部分功能,将编译程序分成前端和后端。

前端:通常将与源程序有关的编译部分称为前端。

词法分析、语法分析、语义分析、中间代码生成、

代码优化 -------分析部分

特点:与源语言有关

后端:与目标机有关的部分称为后端。

目标程序生成(与目标机有关的优化)

-------综合部分

特点:与目标机有关

北京航空航天大学计算机学院

2

2

源程序:多文件、宏定义和宏替换(调用),包含文件

目标程序:一般为汇编程序或可重定位的机器代码

框架源程序

可重定位机器码

(文件组)

预处理器

源程序

库目标、可重

定位目标文件

连接编辑

编译程序

可重定位机器码

目标程序(汇编)

加载器

汇编程序

可运行的机器代码

可重定位机器码

(Obj文件)

北京航空航天大学计算机学院

2

3

š

š

š

š

š

š

语法制导的结构化编辑器

程序格式化工具

软件分析与测试工具

程序理解工具

高级语言的翻译工具

等等

北京航空航天大学计算机学院

2

4

第二章 文法和语言的概念和表示

预备知识 - 形式语言基础

文法和语言的定义

若干术语和重要概念

文法的表示:扩充的BNF范式和语法图

文法和语言的分类

北京航空航天大学计算机学院

1

2

.1 预备知识

一、字母表和符号串

字母表: 符号的非空有限集 例:={a,b,c}

符号: 字母表中的元素 例: a,b,c

符号串: 符号的有穷序列 例:a, aa, ac, abc,..

空符号串:无任何符号的符号串(ε)

符号串的形式定义

有字母表,定义:

1)ε是上的符号串;

2)若x是上的符号串,且a ,则ax或xa是上的符号串;

3)y是上的符号串,iff(当且仅当)y可由(1)和(2)产生。

符号串集合:由符号串构成的集合。

通常约定:

用英文字母表开头的小写字母和字母表靠近末尾

的大写字母来表示符号

如:a, b, c, d, … , r 和 S, T , U, V , W,X, Y, Z

用英文字母表靠近末尾的小写字母来表示符号串

如:s, t, u, v, w , x, y, z

用英文字母表开头的大写字母来表示符号串集合

如:A, B, C, D, … , R

北京航空航天大学计算机学院

3

二、符号串和符号串集合的运算

1

.符号串相等:若x、y是集合上的两个符号串,则x=y

iff(当且仅当)组成x的每一个符号和组成y的每一个符号

依次相等。

2.符号串的长度:x为符号串,其长度|x|等于组成该符

号串的符号个数。

例: x=STV , |x|=3

北京航空航天大学计算机学院

4

3

.符号串的联接:若x、y是定义在Σ上的符号串, 且

x=XY,y=YX,则x和y的联接 xy=XYYX也是Σ上的符号

串。

注意:一般xy≠yx,而εx=xε

4

. 符号串集合的乘积运算:令A、B为符号串集合,

定义 AB={ xy |x∈A, y∈B}

例:A={s, t}, B = {u,v}, AB= ?

su,sv,tu,tv}

{

因为εx=xε=x,所以{ε}A=A {ε} =A

北京航空航天大学计算机学院

5

5

. 符号串集合的幂运算:有符号串集合A,定义

A0 ={ε}, A1=A, A2=AA, A3=AAA,

… …… An=An-1A=AAn-1 ,n>0

6

.符号串集合的闭包运算:设A是符号串集合,定义

A=A1 ∪ A2 ∪ A3 ∪……∪ An ∪……

称为集合A的正闭包。

A*=A0 ∪A

称为集合A的闭包。

例:A={x,y}

A ={x,y, xx,xy,yx,yy , xxx,xxy,xyx,xyy,yxx,yxy,yyx,yyy, ……}

A1

A2

A3

{

ε, x,y, xx,xy,yx,yy , xxx,xxy,xyx,xyy,yxx,yxy,yyx,yyy, ……}

A*

A0 A1

A2

A3

北京航空航天大学计算机学院

6

为什么对符号、符号串、符号串集合以及它们的运算感兴趣?

若A为某语言的基本字符集 ( 把字符看作符号)

A={a, b, ……z, 0, 1, ……, 9, +, -, ×, _, /, ( , ), =, ……}

B为单词集

(单词是符号串)

B ={begin, end, if, then, else, for, ……,<标识符>,<常量>, ……}

则B  A*

把单词看作符号,句子便是符号串)

语言的句子是定义在B上的符号串。

若令C为句子集合,则C  B * , 程序 C

北京航空航天大学计算机学院

7

2

.2文法的非形式讨论

1.什么是文法:文法是对语言结构的定义与描述。即从形式上

用于描述和规定语言结构的称为“文法”(或称为“语法”)。

例:有一句子:“我是大学生” 。这是一个在语法、

语义上都正确的句子,该句子的结构(称为语法结构)是由

它的语法决定的 。在本例中它为“主谓结构”。

如何定义句子的合法性?

有穷语言

无穷语言

北京航空航天大学计算机学院

8

2.语法规则:我们通过建立一组规则,来描述句子的语法结构。

规定用“::=”表示“由...组成”(或“定义为...”)。

<

句子>::=<主语><谓语>

<

<

<

<

<

<

主语>::=<代词>|<名词>

代词> ::=你|我|他

名词>::= 王民|大学生|工人|英语

谓语>::=<动词><直接宾语>

动词>::=是|学习

直接宾语>::=<代词>|<名词>

北京航空航天大学计算机学院

9

3

. 由规则推导句子:有了一组规则之后,可以按照一定的方式

用它们去推导或产生句子。

推导方法:从一个要识别的符号开始推导,即用相应规则的

右部来替代规则的左部,每次仅用一条规则去进行推导。

<

句子> => <主语><谓语>

<

主语> <谓语> => <代词> <谓语>

… ……

这种推导一直进行下去,直到所有带< >的符号都由终结符号

替代为止。

北京航空航天大学计算机学院

1

0

推导方法:从一个要识别的符号

开始推导,即用相应规则的

右部来替代规则的左部,

每次仅用一条规则去进行推导。

<

句子> => <主语><谓语>

=

=

> < 代词><谓语>

> 我<谓语>

=

=

=

=

>我<动词><直接宾语>

>我是<直接宾语>

>我是<名词>

>我是大学生

北京航空航天大学计算机学院

11

例:有一英语句子:The big elephant ate the peanut.

<

句子>::=<主语><谓语>

<

<

<

<

<

<

<

<

主语>::=<冠词><形容词><名词>

冠词> ::=the

形容词>::=big

名词>::=elephant

谓语>::=<动词><宾语>

动词>::=ate

宾语>::=<冠词><名词>

名词> ::=peanut

北京航空航天大学计算机学院

1

2

<

句子> => <主语><谓语>

=

=

=

=

=

=

=

=

=

> <冠词><形容词><名词><谓语>

> the <形容词><名词><谓语>

> the big <名词> <谓语>

> the big elephant <谓语>

> the big elephant <动词><宾语>

> the big elephant ate <宾语>

> the big elephant ate <冠词><名词>

> the big elephant ate the <名词>

> the big elephant ate the peanut

北京航空航天大学计算机学院

1

3

+

上述推导可写成<句子> => the big elephant ate the peanut

说明:

(1) 有若干语法成分同时存在时,我们总是从最左的语法成

分进行推导,这称之为最左推导,类似的有最右推导(还有一般

推导)。

(2) 从一组语法规则可推出不同的句子,如以上规则还可推出

大象吃象”、“大花生吃象”、“大花生吃花生”等句子,

它们 在语法上都正确,但在语义上都不正确。

所谓文法是在形式上对句子结构的定义与描述,而未

涉及语义问题。

北京航空航天大学计算机学院

1

4

4

.语法(推导)树:我们用语法(推导)树

来描述一个句子的语法结构。

<

句子>

<

主语>

<谓语>

<

冠词> <形容词> <名词> <动词>

<

宾语>

冠词> <名词>

the peanut

<

The

big

elephant ate

北京航空航天大学计算机学院

1

5

2.3 文法和语言的形式定义

2

.3.1文法的定义

定义1. 文法G=(Vn,Vt,P,Z)

Vn:非终结符号集

Vt:终结符号集

P:产生式或规则的集合

Z:开始符号(识别符号) Z∈Vn

规则的定义:

规则是一个有序对(U, x), 通常写为:

U :: x 或U  x , | U| = 1 |x|  0

北京航空航天大学计算机学院

1

6

例:无符号整数的文法:

G[<无符号整数>]=(Vn,Vt,P,Z)

Vn={<无符号整数>,<数字串>, <数字>}

Vt = {0, 1, 2, 3, …… 9}

P = {<无符号整数> →<数字串> ,

<

<

<

<

数字串> → <数字串> <数字>,

数字串> →<数字> ,

数字> →0,

数字> →1,

………

<

数字> →9}

Z = <无符号整数>

北京航空航天大学计算机学院

1

7

几点说明:

产生式左边符号构成集合Vn,且Z ∈Vn

有些产生式具有相同的左部,可以合在一起

例:<无符号整数> →<数字串>

<

<

数字串> → <数字串> <数字> | <数字>

数字> →0| 1 | 2 | 3 | …… | 9

给定一个 文法,需给出产生式(规则)集合,并指定识别符号

例:G[<无符号整数>]:

<

<

<

无符号整数> →<数字串>

数字串> → <数字串> <数字> | <数字>

数字> →0| 1 | 2 | 3 | …… | 9

北京航空航天大学计算机学院

1

8

2

.3.2 推导的形式定义

定义2:文法G:v=xUy,w=xuy,

其中x、y ∈V* ,U∈Vn , u ∈V*,

若U :: u∈P,则v w。

G

若x=y=ε,有U :: u,则U  u

G

根据文法和推导定义,可推出终结符号串,所谓通过文法

能推出句子来。

北京航空航天大学计算机学院

1

9

例如:G[<无符号整数>]

(4) <数字> →0

(5) <数字> →1

…………

(1) <无符号整数> →<数字串>

(2) <数字串> → <数字串> <数字>

(3) <数字串> → <数字>

(13) <数字> →9

<

无符号整数>==> <数字串> ==> <数字串> <数字>

(1)

(2)

<

1

数字><数字> =(4=) > 1 <数字>

=

=>

(3)

=

=>

0

(5)

当符号串已没有非终结符号时,推导就必须终止。因为

终结符不可能出现在规则左部,所以将在规则左部出现的符

号称为非终结符号。

北京航空航天大学计算机学院

2

0

定义3:文法G,u , u , u , ……,u ∈V+

0

1

2

n

=

=> ==>

==>

u ==>…… u =w

if v= u0

u

1

2

n

G

G

G

G

则v ==> w

G

例:<无符号整数>==> <数字串> ==> <数字串> <数字>

=

=> <数字><数字> ==>1 <数字>

=

=>1 0

即<无符号整数>==> 10

G

北京航空航天大学计算机学院

2

1

定义4:文法G,有v,w ∈V+

*

if v w , 或v=w,则v => w

=

>

G

G

*

定义5:规范推导:有xUy ==> xuy, 若 y ∈V ,则此推导为规范

t

|

的,记为xUy ==> xuy

直观意义:规范推导=最右推导

最右推导:若规则右端符号串中有两个以上的非终结符时,先推右边的。

最左推导:若规则右端符号串中有两个以上的非终结符时,先推左边的。

+

若有v = u =|=> u =|=> u =|=>……=|=> u = w, 则 v =|=> w

0

1

2

n

北京航空航天大学计算机学院

2

2

2

.3.3 语言的形式定义

定义6:文法G[Z]

1)句型:x是句型 Z * x , 且x∈V* ;

+

2)句子:x是句子 Z  x , 且x∈V * ;

t

+

3)语言:L(G[Z])={x | x∈V *, Z  x } ;

t

形式语言理论可以证明以下两点:

1)G →L(G);

2)L(G) →G1,G2,……,Gn;

已知文法,求语言,通过推导;

已知语言,构造文法,无形式化方法,更多是凭经验。

北京航空航天大学计算机学院

2

3

例:{ abna | n≥1},构造其文法

G1[Z]:

Z→aBa,

B→b | bB

G2[Z]:

Z→aBa,

B→b | Bb

定义7. G和G’是两个不同的文法,若L(G) = L(G’) ,

则G和G’为等价文法。

北京航空航天大学计算机学院

2

4

编译感兴趣的问题是:

给定句子 x 以及文法 G , 求x  L(G) ?

G

yes

x 算法1

算法2 停机

x L(G) ?

no

出错处理

北京航空航天大学计算机学院

2

5

2

.3.4 递归文法

1

.递归规则:规则右部有与左部相同的符号(非终结符)

对于U::= xUy

若x=ε, 即U::= Uy, 左递归

若y=ε, 即U::= xU, 右递归

若x, y≠ε,即U::= xUy,自嵌入递归

2

.递归文法:文法G,存在U ∈Vn

+

if U==>…U…, 则G为递归文法;

+

if U==>U…, 则G为左递归文法;

+

if U==>…U, 则G为右递归文法。

北京航空航天大学计算机学院

2

6

3

. 递归文法的优点:可用有穷条规则,定义无穷语言

会造成死循环(后面将详细论述)

4

. 左递归文法的缺点:不能用自顶向下的方法来进行语法分析

例:对于前面给出的无符号整数的文法是左递归文法,用13

条规则就可以定义出所有的无符号整数。若不用递归文法,那

将要用多少条规则呢?

<

<

<

无符号整数> →<数字串>

数字串> → <数字串> <数字> | <数字>

数字> →0| 1 | 2 | 3 | …… | 9

北京航空航天大学计算机学院

2

7

2

.3.5 句型的短语、简单短语和句柄

定义8. 给定文法G[Z], w = xuy∈V+,为该文法的句型,

*

若Z ==> xUy, 且U ==>u, 则u是句型w相对于U的短语;

*

若Z ==> xUy, 且U ==>u, 则u是句型w相对于U的简单短语。

其中U ∈Vn,u ∈V+,x , y ∈V*

直观理解:短语是前面句型中的某个非终结符所能推

出的符号串。

任何句型本身一定是相对于识别符号Z的短语

北京航空航天大学计算机学院

2

8

定义9. 任一句型的最左简单短语称为该句型的句柄。

给定句型找句柄的步骤:

短语

简单短语 句柄

例: 文法G[<无符号整数>], w = <数字串>1

<

无符号整数> => <数字串> => <数字串><数字>

=> <数字串>1

短语:<数字串>1,1 ; 简单短语:1;句柄:1

注意:短语、简单短语是相对于句型而言的,一个句型

可能有多个短语、简单短语,而句柄只能有一个。

北京航空航天大学计算机学院

2

9

Z

2.4 语法树与二义性文法

U

V

2

.4.1 推导与语法(推导)树

a

b c

d

1)语法(推导)树:句子( 句型)结构的图示表示法,

它是有向图,由结点和有向边组成。

结点:符号

根结点: 识别符号(非终结符)

中间结点:非终结符

叶结点: 终结符或非终结符

有向边:表示结点间的派生关系

北京航空航天大学计算机学院

3

0

( 2 ) 句型的推导及语法树的生成(自顶向下)

给定G[Z],句型w:

*

G

可建立推导序列,Z ==> w

可建立语法树,以Z为树根结点,每步推导生成语法树

的一枝,最终可生成句型w的语法树。

注意一个重要事实:文法所能产生的句子,可以

用不同的推导序列(使用产生式顺序不同)将其

推导出来。语法树的生长规律不同,但最终生成的语

法树形状完全相同。某些文法有此性质,而某些文法

不具此性质。

北京航空航天大学计算机学院

3

1

G[<无符号整数>]:

<

<

<

无符号整数> →<数字串>

数字串> →<数字串> <数字> | <数字>

数字> →0 | 1 | 2 | 3 | …… | 9

一般推导:

<

无符号整数>

(1)

数字串>

<

(2)

<

数字串> <数字>

(3)

(4)

<

数字>

0

(5)

1

北京航空航天大学计算机学院

3

2

最左推导:

<

无符号整数>

(1)

数字串>

<

(2)

<

数字串> <数字>

(3)

(5)

<

数字>

0

(4)

1

北京航空航天大学计算机学院

3

3

最右推导

<

无符号整数>

(1)

数字串>

<

(2)

<

数字串> <数字>

(4)

(3)

<

数字>

0

(5)

1

北京航空航天大学计算机学院

3

4

( 3 ) 子树与短语

子树:语法树中的某个结点(子树的根)连同它向下

派生的部分所组成。

某子树的末端结点按自左向右顺序为句型中的符

定理

号串,则该符号串为该句型的相对于该子树根的短语。

只需画出句型的语法树,然后根据子树找短语→

简单短语→句柄。

北京航空航天大学计算机学院

3

5

( 4 ) 树与推导

句型推导过程<==> 该句型语法树的生长过程

由推导构造语法树

1

从识别符号开始,自左向右建立推导序列。

由根结点开始,自上而下建立语法树。

北京航空航天大学计算机学院

3

6

由语法树构造推导

2

自下而上地修剪子树的某些末端结点(短语),直至

把整棵树剪掉(留根),每剪一次对应一次归约。

从句型开始,自右向左地逐步进行归约,建立推导序列。

北京航空航天大学计算机学院

3

7

定义12. 对句型中最左简单短语(句柄)进行的归约称为

规范归约。

定义13. 通过规范推导或规范归约所得到的句型称为规范句型。

句型<数字><数字>不是文法的规范句型,因为:

<

无符号整数>==><数字串>

=

=

=><数字串><数字>

=><数字><数字>

不是规范推导

北京航空航天大学计算机学院

3

8

2

.4.2 文法的二义性

定义14.1 若对于一个文法的某一句子(或句型)

存在两棵不同的语法树,则该文法是二义性文法,

否则是无二义性文法。

换而言之,无二义性文法的句子只有一棵语法树,尽管推

导过程可以不同。

二义性文法举例:

G[E]:

E ::= E+E | E*E | (E) | i

Vn={E}

Vt={ +, * , ( , ) , i }

北京航空航天大学计算机学院

3

9

对于句子S=i+i * i ∈ L( G[E] ),存在不同的规

范推导:

(1) E==>E+E==>E+E*E ==>E+E*i ==>E+i*i ==> i+i * i

(2) E==> E*E ==> E*i ==> E+E*i ==> E+i*i ==> i+i * i

这两种不同的推导对应了两棵不同的语法树:

E

E

E + E

E * E

i

i

E

i

E

i

E

i

E

i

*

+

北京航空航天大学计算机学院

4

0

定义14.2 若一个文法的某句子存在两个不同的规范推导,则

该文法是二义性的,否则是无二义性的。

(1) E=|=>E+E=|=>E+E*E =|=>E+E*i =|=>E+i*i =|= > i+i * i

|

|

|

|

|

(2) E==> E*E ==> E*i ==> E+E*i ==> E+i*i ==> i+i * i

从自底向上的归约过程来看,上例中规范句型

E+E*i 是由i+i * i通过两步规范归约得到的,但对于同

一个句型E+E* i,它有两个不同的句柄(对应上述两棵

不同的语法树):i 和E+E。因此,文法的二义性意味

着句型的句柄不唯一。

北京航空航天大学计算机学院

4

1

E

E

E + E

E * E

E

E

i

*

i

E + E

句柄:i

句柄:E+E

定义14.3 若一个文法的某规范句型的句柄不唯一,则该文法

是二义性的,否则是无二义性的。

北京航空航天大学计算机学院

4

2

若文法是二义性的,则在编译时就会产生不确定性,

遗憾的是在理论上已经证明:文法的二义性是不可判定的,

即不可能构造出一个算法,通过有限步骤来判定任一文法

是否有二义性。

现在的解决办法是:提出一些限制条件,称为无二义性

的充分条件,当文法满足这些条件时,就可以判定文法是无

二义性的。

例:算术表达式的文法

E::= E+T | T

T ::= T*F | F

F ::= (E) | i

E::= E+E | E*E | (E) | i

北京航空航天大学计算机学院

4

3

句子:i+ i * i

E

E + T

E =|=> E+T =|=> E+T*F =|=> E+T*i

=

=

|=> E+F*i =|=> E+i*i =|=> T+i*i

|=> F+i*i =|=> i+i*i

T

F

T

F

F

i

*

i i

北京航空航天大学计算机学院

4

4

也可以采用另一种解决办法:即不改变二义性文法,而是确

定一种编译算法,使该算法满足无二义性充分条件。

例: Pascal 条件语句的文法

<

条件语句>::= If <布尔表达式>then<语句> |

If <布尔表达式> then <语句> else <语句>

<

语句> ::= <条件语句> | <非条件语句> |…….

If B then If B then stmt else stmt

北京航空航天大学计算机学院

4

5

2.5 句子的分析

任务:给定G[Z]: S ∈ Vt*, 判定是否有S  L (G[Z] ) ?

这是词法分析和语法分析所要做的工作,将在第三、

第四章中详细介绍。

北京航空航天大学计算机学院

4

6

2.6 有关文法的实用限制

若文法中有如U::=U的规则,则这就是有害规则,它会引

起二义性。

例如存在U::=U,U::= a | b,则有两棵语法树:

U

U

a

U

a

北京航空航天大学计算机学院

4

7

多余规则:(1)在推导文法的所有句子中,始终用不到的规则。

即该规则的左部非终结符不出现在任何句型中(不可达符号)

2)在推导句子的过程中,一旦使用了该规则,将推

不出任何终结符号串。即该规则中含有推不出任何终结符号串

的非终结符(不活动符号)

例如给定G[Z],若其中关于U的规则只有如下一条:

U::=xUy

若还有U::=a,则此规则

并非多余

该规则是多余规则。

若某文法中无有害规则或多余规则,则称该文法是压缩过的。

北京航空航天大学计算机学院

4

8

例1:G[<Z>] :

例2: G[S] :

S ::= ccc

<

<

<

<

<

Z> ::= <B> e

A> ::= <A> e | e

B> ::= <C> e | <A> f

C> ::= <C> f

D> ::= f

S ::= Abccc

A ::= Ab

A ::= aBa

B ::= aBa

B ::= AD

D ::= Db

D ::= b

北京航空航天大学计算机学院

4

9

2

.7 文法的其它表示法

<

<

标识符>::=字母{字母|数字}

无符号整数>::=数字{数字}

1

、扩充的BNF表示

BNF的元符号:< , >, ::= , |

扩充的BNF的元符号:< , >, ::= , | , { , } , [ , ] , ( , )

标识符

2

、语法图

字母

字母

无符号整数

数字

北京航空航天大学计算机学院

5

0

2.8 文法和语言分类

形式语言:用文法和自动机所描述的没有语义的语言。

文法定义:乔姆斯基将所有文法都定义为一个四元组:

G=(Vn,Vt,P,Z)

Vn:非终结符号集

Vt:终结符号集

P:产生式或规则的集合

Z:开始符号(识别符号) Z∈Vn

+

语言定义: L(G[Z])={x| x∈Vt*, Z==>x }

北京航空航天大学计算机学院

5

1

文法和语言分类:0型、1型、2型、3型

这几类文法的差别在于对产生式(语法规则)施加不同的限制。

0

型: P:u ::= v

其中 u∈V,v∈V* V = Vn∪ Vt

0型文法称为短语结构文法。规则的左部和右部都可

以是符号串,一个短语可以产生另一个短语。

0

型语言:L0 这种语言可以用图灵机(Turing)接受。

北京航空航天大学计算机学院

5

2

1

型: P:xUy ::= xuy

其中U∈Vn,

x、y、u∈V*

称为上下文敏感或上下文有关。也即只有在x、y这样的

上下文中才能把U改写为u

1

型语言:L1 这种语言可以由一种线性界限自动机接受。

北京航空航天大学计算机学院

5

3

2

型: P:U ::= u

其中U∈Vn,

u∈V*

称为上下文无关文法。也即把U改写为u时,不必考虑上下文。

(1型文法的规则中x,y均为 ε 时即为2型文法)

注意:2型文法与BNF表示相等价。

2

型语言:L2 这种语言可以由下推自动机接受。

北京航空航天大学计算机学院

5

4

3

型文法:

左线性)

P:U ::= t

(右线性)

P:U ::= t

或 U ::= Wt

其中U、W∈Vn

t∈Vt

或 U ::= tW

其中U、W∈Vn

t∈Vt

3

型文法称为正则文法。它是对2型文法进行进一步限制。

3型语言:L3 又称正则语言、正则集合

这种语言可以由有穷自动机接受。

北京航空航天大学计算机学院

5

5

根据上述讨论,L0  L1  L2  L3

0型文法可以产生L0、L1、L2、L3,

但2型文法只能产生L2,L3不能产生L0,L1

3型文法只能产生L3

北京航空航天大学计算机学院

5

6

第三章 词法分析

词法分析程序的功能及实现方案

单词的种类及词法分析程序的输出形式

正则文法和状态图

词法分析程序的设计与实现

正则表达式与有穷自动机

词法分析程序的自动生成器

北京航空航天大学计算机学院

1

3

.1 词法分析程序的功能及实现方案

™

词法分析程序的功能

¨

¨

¨

词法分析:根据词法规则识别及组合单词,进行词法检查。

对数字常数完成数字字符串到二进制数值的转换。

删去空格字符和注释。

北京航空航天大学计算机学院

2

实现方案:基本上有两种

1

.词法分析单独作为一遍

单词串

第二遍

第一遍

S.P.(字符串)

词法分析

S.P.(符号串)

语法分析

优点: 结构清晰、

各遍功能单一

缺点:效率低

2

.词法分析程序作为单独的子程序

取单词

词法分

析程序

语法分

析程序

S.P.(字符串)

单词

优点: 效率高

北京航空航天大学计算机学院

3

3

.2 单词的种类及词法分析程序的输出形式

单词的种类

1

2

3

4

. 保留字:begin、end、for、do...

. 标识符:由用户定义,表示各种名字

. 常 数:无符号数、布尔常数、字符串常数等

. 分界符:+、-、*、/、...

北京航空航天大学计算机学院

4

词法分析程序的输出形式-----单词的内部形式

单词类别 单词值

几种常用的单词内部形式:

1

2

3

、按单词种类分类

、保留字和分界符采用一符一类

、标识符和常数的单词值又为指示字(指针值)

北京航空航天大学计算机学院

5

1

、按单词种类分类

单词名称

单词值

类别编码

标识符

1

2

3

4

5

6

7

内部字符串

整数值

无符号常数(整)

无符号浮点数

布尔常数

字符串常数

保留字

数值

0 或 1

内部字符串

保留字或内部编码

分界符或内部编码

分界符

北京航空航天大学计算机学院

6

2

、保留字和分界符采用一符一类

类别编码

单词名称

单词值

1

2

3

4

5

6

7

8

9

标识符

内部字符串

整数值

无符号常数(整)

无符号浮点数

布尔常数

字符串常数

BEGIN

END

FOR

DO

数值

0 或 1

内部字符串

-

-

-

-

2

2

2

2

….

:

+

*

,

……

…..

-

-

-

0

1

2

3

-

….

(

--

北京航空航天大学计算机学院

7

3

.3 正则文法和状态图

状态图的画法(根据文法画出状态图)

例如:正则文法

Z::= U0 |V1

U ::=Z1 |1

V ::=Z0 | 0

左线性文法。该文法所定义的语言为:

L(G[Z]) = { Bn | n>0 }, 其中B= {01,10}

北京航空航天大学计算机学院

8

左线性文法的状态图的画法:

1

2

. 令G的每个非终结符都是一个状态;

. 设一个开始状态S;

T

3

. 若Q::=T, Q ∈Vn,T ∈Vt, 则:

Q

S

T

4

. 若Q::=RT, Q、R∈Vn,T ∈Vt, 则:

Q

R

5

. 按自动机方法,可加上开始状态和终止状态标志。

北京航空航天大学计算机学院

9

例如:正则文法

Z::= U0 |V1

U ::=Z1 |1

V ::=Z0 | 0

1

Start

其状态图为:

S

U

Z

0

1

0

0

V

1

北京航空航天大学计算机学院

1

0

识别算法

利用状态图可按如下步骤分析和识别字符串x:

1

、置初始状态为当前状态,从x的最左字符开始,

重复步骤2,直到x右端为止。

2

、扫描x的下一个字符,在当前状态所射出的弧中

找出标记有该字符的弧,并沿此弧过渡到下一个状

态;如果找不到标有该字符的弧,那么x不是句子,

过程到此结束;如果扫描的是x的最右端字符,并

从当前状态出发沿着标有该字符的弧过渡到下一个

状态为终止状态Z,则x是句子。

例:x=0110 和1011

北京航空航天大学计算机学院

11

•问题:

1、上述分析过程是属于自底向上分析?还是自顶向下

分析?

Z

2

、怎样确定句柄?

V

Z

U

Z =|=> V1 =|=>Z01 =|=>U001 =|=> 1001

1

0 0 1

北京航空航天大学计算机学院

1

2

3

.4 词法分析程序的设计与实现

词法规则 状态图 词法分析程序

北京航空航天大学计算机学院

1

3

3

.4.1 文法及其状态图

语言的单词符号

标识符

保留字(标识符的子集)

无符号整数

单分界符 + * :,( )

双分界符 :=

两点说明:1. 空格的作用

2. 实数的表示

北京航空航天大学计算机学院

1

4

文法:1. <标识符>::= 字母| <标识符>字母| <标识符>数字

2. <无符号整数>::=数字| <无符号整数>数字

3. <单字符分界符>::= : | + | * | , | ( | )

4. <双字符分界符>::= <冒号>=

5. <冒号>::= :

字母、数字

字母

数字

非字母数字

非数字

标识符

S

数字

无符号整数

S

S

单字符分界符

其他字符

+ * ,

( )

单界

非 =

双字符分界符

=

S

双界

冒号

其他字符

查保留字表

字母、数字

非字母数字

读字符

字母

数字

S

标识符

数字

非数字

无符号整数

其他字符

+

* ,

( )

单界

单字符分界符

双字符分界符

非 =

=

冒号

双界

其他字符

其他

出错

返回S

北京航空航天大学计算机学院

1

6

3

.4.2 状态图的实现——构造词法分析程序

1

2

3

. 单词及内部表示

. 词法分析程序需要引用的公共(全局)变量和过程

. 词法分析程序算法

北京航空航天大学计算机学院

1

7

1

.单词及内部表示: 保留字和分界符采用一符一类

单词名称

类别编码

记忆符

单词值

BEGIN

END

FOR

DO

1

2

3

4

BEGINSY

ENDSY

FORSY

DOSY

-

-

-

-

IF

5

IFSY

-

THEN

ELSE

标识符

6

7

8

THENSY

ELSESY

IDSY

-

-

内部字符串

整数值

常数(整)

9

INTSY

:

10

11

12

13

14

15

16

COLONSY

PLUSSY

STARSY

COMSY

LPARSY

RPARSY

ASSIGNSY

-

-

-

-

-

-

-

+

*

,

(

)

:

=

北京航空航天大学计算机学院

1

8

2

.词法分析程序需要引用的公共(全局)变量和过程

名称

类别

功能

4

4

4

4

CHAR

TOKEN

GETCHAR

GETNBC

字符变量

字符数组

存放当前读入的字符

存放单词字符串

读字符过程 读字符到CHAR,移动指针

过程

反复调用GETCHAR,

直至CHAR进入一个非空白字符

CHAR与TOKEN连接

判断

4

4

CAT

ISLETTER 和

过程

布尔函数

过程

ISDIGIT

4

4

读字符指针后退一个字符

判断TOKEN中的字符串

是保留字, 还是标识符

字符串到数字的转换

出错处理

UNGETCH

RESERVE

布尔函数

函数

过程

4

4

ATOI

ERROR

北京航空航天大学计算机学院

1

9

3

、词法分析程序算法

S TA RT: TOKEN := ‘ ‘; /*置TOKEN为空串*/

GETCHAR; GETNBC;

CASE CHAR OF

A’..’Z’: BEGIN

WHILE ISLETTER OR ISDIGET DO

BEGIN CAT; GETCHAR END;

UNGETCH;

C:= RESERVE;

IF C=0 THEN RETURN(‘IDSY’: TOKEN)

ELSE RETURN (C,-) /*C为保留字编码*/

END;

0’..’9’: BEGIN

WHILE DIGIT DO

BEGIN CAT; GETCHAR END;

UNGETCH;

RETURN (‘INTSY’,ATOI)

END;

+’ : RETURN(‘PLUSSY’,-) ;

北京航空航天大学计算机学院

2

0

*’ : RETURN(‘STARSY’,-) ;

,’ : RETURN(‘COMMASY’,-) ;

(’ : RETURN(‘LPARSY’,-) ;

)’ : RETURN(‘RPARSY’,-) ;

:’ : BEGIN

GETCHAR;

if CHAR=‘=‘ THEN RETURN(‘ASSIGNSY’,-) ;

UNGETCH;

RETURN(‘COLONSY’,-) ;

END

END OF CASE;

ERROR;

GOTO START;

北京航空航天大学计算机学院

2

1

3

.5.1 正则表达式和正则集合的递归定义

有字母表, 定义在 上的正则表达式和正则集合递归定义如下:

1

2

3

. 和都是 上的正则表达式, 其正则集合分别为:{}和;

. 任何a  , a是 上的正则表达式,其正则集合为:{a};

. 假定U和V是  上的正则表达式, 其正则集合分别记为L(U)

和L(V), 那么U|V, U•V和U*也都是 上的正则表达式, 其

正则集合分别为L(U) L(V)、 L(U) • L(V)和L(U)*

. 任何 上的正则表达式和正则集合均由1、2和3产生。

4

北京航空航天大学计算机学院

2

2

正则表达式中的运算符:

|

*

-----或(选择) • ----连接

或 { } ---重复 () ----括号

运算符的优先级:

先*, 后• , 最后 |

• 在正则表达式中可以省略.

正则表达式相等 这两个正则表达式表示的语言相等

如:b{ab} = {ba}b

{a|b} = {{a} {b}} = (a*b*)*

北京航空航天大学计算机学院

2

3

例:设  = { a,b },下面是定义在上的正则表达式和正则集合

正则集合

正则表达式

ba*

a(a|b)*

(a|b)*(aa|bb)(a|b)*

北京航空航天大学计算机学院

2

4

正则表达式的性质:

设e1, e2和e3均是某字母表上的正则表达式, 则有:

单位正则表达式: 

e = e = e

交换律: e1 | e2 = e2 | e1

结合律: e1|(e2|e3) = (e1|e2)|e3

e1(e2e3) = (e1e2)e3

分配律: e1(e2|e3) = e1e2|e1e3

(e1|e2)e3 = e1e3|e2e3

此外: r* = (r|)* r** =r*

(r|s)* = (r*s*)*

北京航空航天大学计算机学院

2

5

正则表达式与3型文法等价

例如:

正则表达式:ba*

a(a|b)*

3

型文法: Z ::= Za|b

Z::=Za|Zb|a

例:

3

型文法

正则表达式

S::= aS|aB

B::= bC

aS|aba*a

ba*a

a*aba*a

C::= aC|a

a*a

北京航空航天大学计算机学院

2

6

3

.5.2 确定的有穷自动机(DFA)— 状态图的形式化

(Deterministic Finite Automata)

一个确定的有穷自动机(DFA)M是一个五元式:

M=(S, Σ,δ,s0, Z)

其中:

1

2

3

. S —有穷状态集

. Σ—输入字母表

. δ—映射函数(也称状态转换函数)

S×Σ→S

δ(s,a)=s’ , s, s’ ∈S, a∈Σ

4

5

. s0 —初始状态

. Z—终止状态集

s

0

∈S

ZS

北京航空航天大学计算机学院

2

7

例如: M:({0,1,2,3},{a,b},δ,0,{3})

δ(0,a)=1 δ(0,b)=2

δ(1,a)=3 δ(1,b)=2

δ(2,a)=1 δ(2,b)=3

δ(3,a)=3 δ(3,b)=3

状态转换函数δ可用一矩阵来表示:

输入

字符

所谓确定的状

态机,其确定

性都表现在状

态转换函数是

单值函数!

状态

a

b

0

1

2

3

1

2

3

1

3

2

3

3

北京航空航天大学计算机学院

2

8

DFA也可以用一状态转换图表示:

DFA的状态图表示:

a

a

1

2

start

0

b

a

3

a,b

b

b

北京航空航天大学计算机学院

2

9

DFA M所接受的符号串:

令α= a1a2¨¨¨an,α∈Σ,若*δ(δ(¨¨ δ(s0, a1),a2)¨¨),

an-1),an) = Sn ,且Sn ∈Z,则可以写成δ(s0, α)= Sn,我们称α可为

M所接受。

δ(s0, a1)= S1

换言之:若存在一条

初始状态到某一终止

状态的路径,且这条

路径上能有弧的标记

符号连接成符号串α,

则称α为DFA M(接受)

识别。

δ(s1, a2)= S2

……

δ(sn-2, an-1)= Sn-1

δ(sn-1, an)= Sn

DFA M所接受的语言为:L(M)={α|δ(s0, α)= Sn , Sn∈Z}

北京航空航天大学计算机学院

3

0

3

.5.3 不确定的有穷自动机(NFA)

(Nondeterministic Finite Automata)

若δ是一个多值函数,且输入可允许为ε,则有穷自动机是不确定的,

即在某个状态下,对于某个输入字符存在多个后继状态。

从同一状态出发,有以同一字符标记的多条边,或者有

以ε标记的特殊边的自动机。

北京航空航天大学计算机学院

3

1

NFA的形式定义为:

一个非确定的有穷自动机NFA M’是一个五元式:

NFA M’=(S, Σ∪{ε}, δ, s0, Z)

其中 S—有穷状态集

Σ∪{ε}—输入符号加上ε,

即自动机的每个结点所射出的弧可以是Σ中的一个字符或是ε

s0—初态

s

0

∈S

Z—终态集

δ—转换函数

ZS

S×Σ∪{ε} →2S

(2S --S的幂集—S的子集构成的集合)

北京航空航天大学计算机学院

3

2

NFA M’所接受的语言为:

L(M’)={α|δ(S0,α)=S’ S’∩Z≠Φ}

例:NFA M’=({1,2,3,4},{a,b,c} ∪{ε},δ,1,{4})

北京航空航天大学计算机学院

3

3

上例题相应的状态图为:

a

b

a

2

start

ε

4

1

c

a 3

c

M’所接受的语言(用正则表达式) R=aa*b|ac*c|ε

北京航空航天大学计算机学院

3

4

3

.5.4 NFA的确定化

已证明:不确定的有穷自动机与确定的有穷自动机从功能

上来说是等价的,也就是说能够从:

NFA M ’构造成一个DFA M

使得 L(M)=L(M’)

北京航空航天大学计算机学院

3

5

为了使得NFA确定化,首先给出两个定义:

定义1、集合I的ε-闭包:

令I是一个状态集的子集,定义ε-closure(I)为:

1

2

)若s∈I,则s∈ε-closure(I);

)若s∈I,则从s出发经过任意条ε弧能够到达的任何

状态都属于ε-closure(I)。

状态集ε-closure(I)称为I的ε-闭包。

可以通过一例子来说明状态子集的ε-闭包的构造方法

北京航空航天大学计算机学院

3

6

ε

例:

2

3

4

6

5

a

如图所示的状态图:

令I={1},

1

ε

a

求ε-closure(I)=?

a

根据定义:

ε-closure(I)= {1,3}

北京航空航天大学计算机学院

3

7

定义2: 令I是NFA M’的状态集的一个子集, a∈Σ

定义: Ia=ε-closure(J)

其中J = ∪δ(s,a)

S∈I

-- J是从状态子集I中的每个状态出发,经过标记为a的弧而

达到的状态集合。

-

- Ia是状态子集,其元素为J中的状态,加上从J中每一个

状态出发通过ε弧到达的状态。

同样可以通过一例子来说明上述定义,仍采用前面给定的

状态图为例

北京航空航天大学计算机学院

3

8

例:令 I={1}

Ia =ε-closure(J)

=

=

=

ε-closure(δ(1,a))

ε-closure({2,4})

{2,4,6}

根据定义1,2,可以将上述的M’确定化(即可构造出状态

转换矩阵)

北京航空航天大学计算机学院

3

9

例:有NFA M’

I=ε-closure({1})={1,4}

Ia=ε-closure(δ(1,a)∪δ(4,a))

a

b

a

2

=

=

=

ε-closure({2,3}∪φ)

ε-closure ({2,3})

{2,3}

start

ε

1

4

c

a 3

Ib= ε-closure(δ(1,b)∪δ(4,b))

c

=

=

ε-closure(φ)

φ

Ic= ε-closure(δ(1,c)∪δ(4,c))

=

φ

I={2,3}, I ={2},I ={4},I ={3,4}…

a

b

c

北京航空航天大学计算机学院

4

0

I

Ia

1,4} {2,3} φ

2,3} {2} {4}

2} {2}

Ib

Ic

{

{

{

{

{

φ

{3,4}

{4} φ

4} φ

φ

φ

φ

3,4} φ

{3,4}

北京航空航天大学计算机学院

4

1

将求得的状态转换矩阵重新编号

DFA M状态转换矩阵:

符号

a

b

_

c

状态

_

0

1

2

3

4

1

2

2

3

3

4

_

_

_

_

_

_

4

北京航空航天大学计算机学院

4

2

DFA M的状态图:

{2,3}

a

c

{1,4}

1

start

0

4

b

3

a

c

{3,4}

a

2

{2}

{4}

b

注意:包含原初始状态1的状态子集为DFA M的初态

包含原终止状态4的状态子集为DFA M的终态。

北京航空航天大学计算机学院

4

3

3

.5.5 正则表达式与DFA的等价性

定理:在Σ上的一个子集V ( VΣ* )是正则集合,当且仅当

存在一个DFA M,使得V=L(M)

V是正则集合,

R是与其相对应的正则表达式  DFA M

V=L(R)

L(M)=L(R)

所以 正则表达式R  NFA M’  DFA M

L(R) = L(M’) = L(M)

证明: 根据定义。

北京航空航天大学计算机学院

4

4

start

start

start

a

a

φ

φ

正则表达式 ε

正则集合 {ε}

{

a}

N(s)

ε

ε

R=s|t NFA(R):

start

x

x

y

y

ε

ε

ε

ε

N(t)

N(t)

ε

R=st NFA(R):

start ε

N(s)

x

R=s* NFA(R):

start

ε

ε

N(s) y

ε

北京航空航天大学计算机学院

4

5

3

.6 词法分析程序的自动生成器—LEX(LEXICAL)

LEX的原理:

正则表达式与DFA的等价性

根据给定的正则表达式自动生成相应的词法分析程序。

LEX的功能:

LEX源程序

S.P.(字符串)

LEX 词法分析程序L

S.P.单词(符号串)

L

北京航空航天大学计算机学院

4

6

3

.6.1 LEX源程序

一个LEX源程序主要由三个部分组成:

1

2

3

. 辅助定义式

. 识别规则

. 用户子程序

各部分之间用%%隔开

北京航空航天大学计算机学院

4

7

辅助定义式是如下形式的LEX语句:

D

D

1

2

R

R

1

2

D

n

R

n

其中:

R1,R2,……,Rn 为正则表达式。

D1,D2,……,Dn 为正则表达式名字,称简名。

北京航空航天大学计算机学院

4

8

例:标识符: letter→A|B|¨¨¨|Z

digit →0|1| ¨¨¨|9

iden →letter(letter|digit)*

带符号整数:integer→digit (digit)*

sign →+| - |ε

sign_integer →sign integer

北京航空航天大学计算机学院

4

9

识别规则:是一串如下形式的LEX语句:

P

P

1

{A1}

{A2}

2

P

m

{Am}

P

i

:定义在Σ∪{D1,D2,¨¨Dn}上的正则表达式,也称词形。

{Ai}:Ai为语句序列,它指出,在识别出词形为Pi的单词以

后,词法分析器所应作的动作。

其基本动作是返回单词的类别编码和单词值。

北京航空航天大学计算机学院

5

0

下面是识别某语言单词符号的LEX源程序:

例:LEX 源程序

AUXILIARY DEFINITIONS /*辅助定义*/

letter  A|B|¨¨|Z

digit  0|1|¨¨|9

%

%

RECOGNITION RULES

/*识别规则*/

1

2

3

.BEGIN

.END

{RETURN(1,─) }

{RETURN(2,─) }

{RETURN(3,─) }

.FOR

北京航空航天大学计算机学院

5

1

4

5

6

7

8

9

.DO

{RETURN(4,─) }

{RETURN(5,─) }

{RETURN(6,─) }

{RETURN(7,─) }

{RETURN(8,TOKEN) }

{RETURN(9,DTB }

{RETURN(10,─) }

{RETURN(11,─) }

{RETURN(12,─) }

.IF

.THEN

.ELSE

.letter(letter |digit)*

.digit(digit)*

1

0. :

1. +

2. “*”

1

1

北京航空航天大学计算机学院

5

2

1

1

1

1

1

3. ,

{RETURN(13,─) }

{RETURN(14,─) }

{RETURN(15,─) }

4. “(”

5. “)”

6. :=

{RETURN(16,─) }

{RETURN(17,─) }

7. =

北京航空航天大学计算机学院

5

3

3

.6.2 LEX的实现

LEX的功能是根据LEX源程序构造一个词法分析程序,

该词法分析器实质上是一个有穷自动机。

LEX生成的词法分析程序由两部分组成:

状态转换矩阵(DFA)

词法分析程序

控制执行程序

LEX的功能是根据LEX源程序生成状态转换矩阵和控制程序

北京航空航天大学计算机学院

5

4

LEX的工作过程:

·

¨

·

扫描每条识别规则Pi,构造相应的不确定有穷自动机Mi

将各条规则的有穷自动机Mi合并成一个新的NFA M

¨确定化

NFADFA

生成该DFA的状态转换矩阵和控制执行程序

北京航空航天大学计算机学院

5

5

LEX二义性问题的两条原则:

1

.最长匹配原则

P

j

P

i

在识别单词过程中,有一字符串 x x x x x

根据最长匹配原则,应识别为这是

P

k

一个符合Pk规则的单词,而不是Pj和Pi规则的单词。

2

.优先匹配原则

如有一字符串,有两条规则可以同时匹配时,那么用规则

序列中位于前面的规则相匹配,所以排列在最前面的规则优先

权最高。

北京航空航天大学计算机学院

5

6

P

1

例:字符串·¨begin·¨

P

8

根据原则,应该识别为关键字begin,所以在写LEX源程序

时应注意规则的排列顺序。另要注意的是,优先匹配原则

是在符合最长匹配的前提下执行的。

可以通过一个例子来说明这些问题:

北京航空航天大学计算机学院

5

7

start

a

例: LEX源程序

a { }

1

2

4

8

start a

b

b

3

7

5

6

abb { }

a

b

start

b

a*bb

*

{ }

一.读LEX源程序,分别生成NFA,用状态图表示为:

a

二.合并成一个NFA:

1

3

7

2

4

8

ε

start ε

a

b

b

0

5

6

a

ε

b

b

北京航空航天大学计算机学院

5

8

三.确定化 给出状态转换矩阵

状态

a

b 到达终态所识别的单词

初态

{2,4,7} {8}

{0,1,3,7}

终态 {2,4,7}

{7}

8} φ

7} {7}

{5,8}

{8}

{8}

{6,8}

a

*

{

a* bb

终态

{

*

终态

终态

{5,8} φ

{6,8} φ

a bb

*

{8}

abb

在此DFA中 初态为{0,1,3,7}

终态为{2,4,7},{8},{5,8},{6,8}

北京航空航天大学计算机学院

5

9

读入字符 进入状态

开始 {0,1,3,7}

词法分析程序的分析过程

令输入字符串为aba…

a

{2,4,7}

(1) 吃进字符ab

(2) 按反序检查状态子集

检查前一次状态是否含有原

b {5,8}

a 无后继状态(退

掉输入字符a)

NFA的终止状态

·即检查{5,8},含有终态8,因此断定所识别的单词ab是属于

a*bb*中的一个。

··若在状态子集中无NFA的终态,则要从识别的单词再退掉

一个字符(b),然后再检查上一个状态子集。

··若一旦吃进的字符都退完,则识别失败,调用出错程序,

一般是跳过一个字符然后重新分析。(应打印出错信息)

·

北京航空航天大学计算机学院

6

0

三点说明:

1

) 以上是LEX的构造原理,虽然是原理性的,但据此就

不难将LEX构造出来。

2

) 所构造出来的LEX是一个通用的工具,用它可以生成

各种语言的词法分析程序,只需要根据不同的语言书

写不同的LEX源文件就可以了。

3

) LEX不但能自动生成词法分析器,而且也可以产生多

种模式识别器及文本编辑程序等。

北京航空航天大学计算机学院

6

1

正则文法

1

5

2

6

4

3

NFA

DFA

正则表达式

最小化

北京航空

6

2

1)有穷自动机正则文法

算法:

t

A

B

1

2

3

.对转换函数f(A,t)=B,可写成一个产生式:A→tB

.对可接受状态Z,增加一个产生式:Z→ε

.有穷自动机的初态对应于文法的开始符号(识别符号),

有穷自动机的字母表为文法的终结符号集。

北京航空航天大学计算机学院

6

3

a

例:给出如图NFA等价的正则文法G

start

a

b

C

A

B

a

G=({A,B,C,D},{a,b},P,A)

b

b

其中P: A aB

D

A→bD

B→bC

b

C aA

C bD

C ε

D→aB

D→bD

D ε

北京航空航天大学计算机学院

6

4

2)正则文法有穷自动机M

算法:

1

2

.字母表与G的终结符号相同;

.为G中的每个非终结符生成M的一个状态,G的开始符号S

是开始状态S;

3

4

.增加一个新状态Z,作为NFA的终态;

.对G中的形如A→tB,其中t为终结符或ε,A和B为非终结

符的产生式,构造M的一个转换函数f(A,t)=B;

5

.对G中的形如A→t的产生式,构造M的一个转换函数

f(A,t)=Z。

北京航空航天大学计算机学院

6

5

例:求与文法G[S]等价的NFA

G[S]: S→aA|bB|ε

A→aB|bA

B→aS|bA|ε

b

A

a

a

求得:

b

start

b

S

B

a

ε

ε

Z

北京航空航天大学计算机学院

6

6

3)正则式有穷自动机

语法制导方法

.(a)对于正则式φ,所构造NFA:

1

x

x

y

y

ε

(b)对于正则式ε,所构造NFA:

(c)对于正则式a,a∈Σ,则NFA:

a

x

y

北京航空航天大学计算机学院

6

7

2

.若s,t为Σ上的正则式,相应的NFA分别为N(s)和N(t);

(a)对于正则式R=s|t, NFA(R)

ε

N(s)

N(t)

ε

ε

x

y

ε

(b)对正则式R=st, NFA(R)

ε

ε

ε

N(s)

N(t)

x

y

或:

x N(s) N(t) y

北京航空航天大学计算机学院

6

8

(c)对于正则式R=s*, NFA(R)

ε

ε

ε

x

N(s) y

ε

(d)对R=(s),与R=s的NFA一样.

北京航空航天大学计算机学院

6

9

*

例:为R=(a|b) abb构造NFA N,使得L(N)=L(R)

a

从左到右分解R,令r1=a,第一个a,则有 2

3

b

令r2=b,则有 4

5

a

令r3= r1 |r2,则有

ε

ε

ε

ε

2

4

3

5

1

6

b

北京航空航天大学计算机学院

7

0

令r4= r3* 则有:

ε

a

ε

ε

ε

ε

2

4

3

5

ε

ε

0

1

6

7

b

ε

令r5=a,

令r6=b

令r7=b

令r8= r5 r6

令r9= r8 r7 则有

a

b

b

 7

8

9

10

北京航空航天大学计算机学院

7

1

令r10= r4 r9 则最终得到NFA N:

ε

a

ε

ε

ε

ε

2

4

3

5

ε

ε a b b

7 8 9 10

0

1

6

b

ε

分解R的方法有很多种,下面给出另一种分解方式和所构成的NFA

北京航空航天大学计算机学院

7

2

(a|b)*

abb

(a)

(b)

x

x

x

i

y

j

a|b

ε ε

abb

abb

i

y

y

a

ε ε

i

j

(c)

(d)

b

a

ε ε

a

b

b

x

i

j

k

l

y

b

北京航空航天大学计算机学院

7

3

4)有穷自动机正则式R

算法:

1

)在M上加两个结点x,y,从x结点用ε弧到M的所

有初态,从M的所有终态用ε到y结点形成与M等

价的M’,M’只有一个初态x和一个终态y。

)逐步消去M’中的所有结点,直至剩下x和y结点,在

消结过程中,逐步用正则式来记弧,其消结规则如

下:

2

R

1

R

2

2 3

RR

1 2

1

2

3

.对于

代之为

1

1 3

R

1

2 代之为 1 R1|R2 2

.对于 1

R

2

R

2

.对于

代之为

R

1

R

3

2 3

R1R2*R3

1

1

3

北京航空航天大学计算机学院

7

4

a,b

a,b

例:M:

start

a

a

0

3

4

b

1

b

2

a,b

a,b

解: (1)加x,y

a

a

3

1

4

a,b

0

ε

ε

y

x

ε

b

2

b

a,b

北京航空航天大学计算机学院

7

5

(2)消除M中的所有结点

a|b

aa(a|b) *

4

a|b

a|b

aa ε

x

0

y

x

0

y

ε

ε

bb ε

2

bb(a|b) *

a|b

(a|b)*(aa|bb)(a|b)*

x

y

北京航空航天大学计算机学院

7

6

5)正则文法正则式

利用以下转换规则,直至只剩下一个开始符号定义的产

生式,并且产生式的右部不含非终结符。

规则 文法产生式 正则式

规则1 A→xB,B→y A=xy

规则2

规则3

A→xA|y

A=x*y

A→x,A→y A=x|y

北京航空航天大学计算机学院

7

7

例:有文法G[s]

S→aA|a,

A→aA|dA|a|d

于是: S=aA|a

A=(aA|dA)|(a|d)A=(a|d)A|(a|d)

由规则二: A=(a|d)*(a|d)

代入:S=a(a|d)*(a|d)|a

于是:S=a((a|d)*(a|d)|ε)

北京航空航天大学计算机学院

7

8

6)正则式正则文法

算法:

1

) 对任何正则式r,选择一个非终结符S作为识别符号,

并产生产生式 S→r

2

) 若x,y是正则式,对形为A→xy的产生式,重写为

A→xB B→y,其中B为新的非终结符,B∈Vn

同样: 对于 A→x*y  A→xA A→y

A→x|y  A→x A→y

例:将R=a(a|d)*转换成相应的正则文法

解:1) S→a(a|d)*

3

) S→aA

A→(a|d)A

A→ε

4) S→aA

A→aA|dA

A→ε

2

) S→aA

A→(a|d)*

北京航空航天大学计算机学院

7

9

补充: DFA的简化(最小化)

“对于任一个DFA,存在一个唯一的状态最少的等价的DFA”

一个有穷自动机是化简的 它没有多余状态并且它的状态

中没有两个是互相等价的。

一个有穷自动机可以通过消除多余状态和合并等价状态

而转换成一个最小的与之等价的有穷自动机

北京航空航天大学计算机学院

8

0

定义:

(1) 有穷自动机的多余状态:从该自动机的开始状态出发,任

何输入串也不能到达那个状态

0

1

例:

画状态图可

以看出s4,s6,s8

为不可达状

态应该消除

s

s

s

s

s

s

s

s

s

0

1

2

3

4

5

6

7

8

s

s

s

s

s

s

s

s

s

1

s

s

s

s

s

s

s

s

s

5

0

1

2

2

5

5

3

8

0

3

7

5

7

6

1

0

1

6

s

s

s

s

s

s

0

1

2

3

5

7

s

s

s

s

s

s

1

s

s

s

s

s

s

5

2

2

5

3

0

7

5

7

1

1

北京航空航天大学计算机学院

8

1

(2)等价状态 状态s和t的等价条件是:

1

2

)一致性条件:状态s和t必须同时为可接受状态或

不接受状态。

)蔓延性条件:对于所有输入符号,状态s和t必须

转换到等价的状态里。

对于所有输入符号c,Ic(s)=Ic(t),即状态s、t对于c具有相同

的后继,则称s,t是等价的。

(任何有后继的状态和任何无后继的状态一定不等价)

有穷自动机的状态s和t不等价,称这两个状态是可区别的。

北京航空航天大学计算机学院

8

2

分割法”:把一个DFA(不含多余状态)的状态分割成一些

不相关的子集,使得任何不同的两个子集状态

都是可区别的,而同一个子集中的任何状态都

是等价的。

b

例:最小化

a

a

6

4

a

a

a

b

start

7

1

5

a

b

a

b

b

b

2

3

b

北京航空航天大学计算机学院

8

3

解:

(一)区分终态与非终态

a

6

7

b 区号

区号

a

6

7

b

3

3

1

2

3

3

1

2

1

1

3

4

1

4

5

6

3

3

4

1

4

5

6

3

2

5

6

7

4

5

6

7

4

1 2

1 3

7

4

2

7

4

2

北京航空航天大学计算机学院

8

4

区号

区号

a

6

7

b

3

3

a

1 6

2 7

b

3

3

1

2

1

1

3

4

1

4

5 2

6 3

3 1

4 4

5 2

6 3

4

5

6

7

4

3

5 7

6 4

3

1

1 4

5

7

4

2

7 4

2

北京航空航天大学计算机学院

8

5

将区号代替状态号得:

a

b

b

a

a

5

a

1

2

3

4

5

5

1

3

5

3

2

4

5

2

1

3

a

b

1

a

b

b

2

4

b

北京航空航天大学计算机学院

8

6

语法分析的功能、基本任务

自顶向下分析法

自底向上分析法

北京航空航天大学计算机学院

1

4.1 语法分析概述

功能:根据文法规则,从源程序单词符号串中识别出语法

成分,并进行语法检查。

基本任务:识别符号串S是否为某语法成分。

两大类分析方法:

自顶向下分析

自底向上分析

北京航空航天大学计算机学院

2

自顶向下分析算法的基本思想为:

+

若Z  S 则 S  L(G[Z]) 否则 S  L(G[Z])

G[Z]

s 主要问题:

Ø 左递归问题

§ 主要方法:

递归子程序法

LL分析法

Ø 回溯问题

北京航空航天大学计算机学院

3

自底向上分析算法的基本思想为:

+

若Z  S

则 S L(G[Z]) 否则 S  L(G[Z])

G[Z]

s 主要问题:

§ 主要方法:

算符优先分析法

LR分析法

Ø 句柄的识别问题

北京航空航天大学计算机学院

4

4.2 自顶向下分析

4

.2.1 自顶向下分析的一般过程

给定符号串S,若预测是某一语法成分,则可根据该

语法成分的文法,设法为S构造一棵语法树,

若成功,则S最终被识别为某一语法成分,即

SL(G[Z]),其中G[Z]为某语法成分的文法

若不成功, 则 SL(G[Z])

可以通过一例子来说明语法分析过程

北京航空航天大学计算机学院

5

例:

S = cad

G[Z]:

分析过程是设法建立一

棵语法树,使语法树的末端结

点与给定符号串相匹配。

Z∷ =cAd

A∷ =ab|a

求解 SL(G[Z]) ?

Z

1

2

. 开始:令Z为根结点

·

Z

. 用Z的右部符号串去匹配输入串

·

完成一步推导ZcAd

检查, c-c匹配

c A d

A是非终结符,将匹配任务交给A

北京航空航天大学计算机学院

6

Z

·

3

. 选用A的右部符号串匹配输入串

A有两个右部,选第一个

c A d

完成进一步推导Aab

检查,a-a匹配,b-d不匹配(失败)

但是还不能冒然宣布SL(G[Z])

a b

Z

4

. 回溯 即砍掉A的子树

改选A的第二右部

·

Aa 检查 a-a匹配

d-d匹配

c A d

a

建立语法树,末端结点为cad,与输入cad相匹配,

建立了推导序列 ZcAdcad

cadL(G(Z))

北京航空航天大学计算机学院

7

自顶向下分析方法特点:

1

2

. 分析过程是带预测的,对输入符号串要预测属于什么

语法成分,然后根据该语法成分的文法建立语法树。

. 分析过程是一种试探过程,是尽一切办法(选用不同

规则) 来建立语法树的过程, 由于是试探过程, 难免

有失败, 所以分析过程需进行回溯, 因此也称这种方法

是带回溯的自顶向下分析方法。

3

. 最左推导可以编写程序来实现, 但带溯的自顶向下分

析方法在实际上价值不大, 效率低。

北京航空航天大学计算机学院

8

4

.2.2 自顶向下分析存在的问题及解决方法

1

、 左递归文法:

有如下文法:

令U是文法的任一非终结符,文法中有规则

+

U∷ =U¨¨或者U U¨¨

这个文法是左递归的。

自顶向下分析的基本缺点是:

不能处理具有左递归性的文法

为什么?

北京航空航天大学计算机学院

9

如果在匹配输入串的过程中,假定正好轮到要用非终结

符U直接匹配输入串,即要用U的右部符号串U¨¨去匹配,

为了用U¨¨去匹配,又得用U去匹配,这样无限的循环下

去将无法终止。

如果文法具有间接左递归,则也将发生上述问题,只不

过环的圈子兜得更大。

要实行自顶向下分析,必须要消除文法的左递归,下面

将介绍直接左递归的消除方法,在此基础上再介绍一般左递

归的消除方法。

北京航空航天大学计算机学院

1

0

消除直接左递归

方法一,使用扩充的BNF表示来改写文法

例:(1) E∷ =E+T|T  E∷ =T{+T}

(2) T∷ =T*F|T/F|F  T ∷ =F{*F|/F}

a. 改写以后的文法消除了左递归。

b. 可以证明,改写前后的文法是等价的,表现在

L(G改前) = L(G改后)

如何改写文法能消除左递归,又前后等价,

可以给出两条规则:

北京航空航天大学计算机学院

11

规则一(提因子)

若:U∷ =xy|xw|….|xz

则可改写为:U∷ =x(y|w|….|z)

若:y=y y , w=y w

1

2

1 2

则 U∷ =x(y (y |w )|….|z)

1

2

2

若有规则:U∷ =x|xy

则可以改写为:U∷ =x(y|ε)

注意:不应写成U∷ =x(ε|y)

使用提因子法,不仅有助于消除直接左递归,而且有

助于压缩文件的长度,使我们能更有效地分析句子。

北京航空航天大学计算机学院

1

2

规则二

若有文法规则:U∷ =x|y|……|z|Uv

其特点是:具有一个直接左递归的右部并位于最后,

这表明该语法类U是由x或y……或z其后随有零个

或多个v组成。

UUv  Uvv  Uv vv  ……

可以改写为U∷ =(x|y|……|z){v}

通过以上两条规则,就能消除文法的直接左递归,

并保持文法的等价性。

北京航空航天大学计算机学院

1

3

方法二,将左递归规则改为右递归规则

规则三

若:P∷ =P| 

则可改写为:P ∷ =  P’

P’ ∷ = P’| ε

北京航空航天大学计算机学院

1

4

例1 E∷ =E+T|T

右部无公因子,所以不能

用规则一。

例2 T∷ =T*F|T/F|F

T∷ =T(*F|/F) | F 规则一

T∷ =F|T(*F|/F)

为了使用规则二,

令E∷ =T|E+T

T∷ =F{(*F|/F)} 规则二

即T∷ =F{*F|/F}

由规则二可以得到

E∷ =T{+T}

右递归:

T ::= FT’

T’ ::= *FT’ | /FT’ | ε

北京航空航天大学计算机学院

1

5

消除一般左递归

一般左递归也可以通过改写文法予以消除。

消除所有左递归的算法:

1

. 把G的非终结符整理成某种顺序A1,A2,……An ,使得:

A

1

::= δ1|δ2|……δk

::= A1 r……

::= A2u |A1v…..

A

A

2

3

….

北京航空航天大学计算机学院

1

6

2

. For i:=1 to n do

begin

for j :=1 to i-1 do

把每个形如Ai∷ =Ajr的规则替换成

∷ =(δ1|δ2|……|δk) r ,

A

i

其中Aj ∷ =δ1|δ2|……|δk是当前全部Aj 的规则;

消除Ai规则中的直接左递归

end

3

. 化简由2得到的文法即可。

北京航空航天大学计算机学院

1

7

例:文法G[s]为

S ∷ = Qc|c

Q ∷ = Rb|b

R ∷ = Sa|a

该文法无直接左递归,但有间接左递归

+

SQc Rbc Sabc

∴ S Sabc

R∷ =Sa|a

Q∷ =Rb|b

S∷ =Qc|c

非终结符顺序重新排列

北京航空航天大学计算机学院

1

8

1

2

3

4

5

. 检查规则R是否存在直接左递归 R∷ =Sa|a

. 把R代入Q的有关选择,改写规则Q

. 检查Q是否存在直接左递归

Q∷ =Sab|ab|b

. 把Q代入S的右部选择

. 消除S的直接左递归

S∷ =Sabc|abc|bc|c

S∷ =(abc|bc|c){abc}

北京航空航天大学计算机学院

1

9

最后得到文法为:

S∷ =(abc|bc|c){abc}

Q∷ =Sab|ab|b

R∷ =Sa|a

可以看出其中关于Q和R的规则是多余的规则

∴经过压缩后 S∷ =(abc|bc|c){abc}

可以证明改写前后的文法是等价的

应该指出,由于对非终结符的排序不同,最后得到的文法在形

式上可能是不一样的,但是不难证明它们的等价。

北京航空航天大学计算机学院

2

0

2

、回溯问题

什么是回溯?

分析工作要部分地或全部地退回去重做叫回溯。

造成回溯的条件:

文法中,对于某个非终结符号的规则其右部

有多个选择,并根据所面临的输入符号不能准确

地确定所要的选择时,就可能出现回溯。

回溯带来的问题:

严重的低效率,只有在理论上的意义而无实际意义

北京航空航天大学计算机学院

2

1

效率低的原因

1

FIRST(α1)

1

2

) 语法分析要重做

FIRST(α2)

U

2

) 语义处理工作要推倒重来

FIRST(α2)

设文法G(不具左递归性),U Vn

U::=  |  | 

3

1

2

3

[

定义] FIRST(αi) = {a | αi*

 a…, a Vt }

为避免回溯,对文法的要求是:

FIRST(αi) ∩ FIRST(αj)=φ (ij)

北京航空航天大学计算机学院

2

2

消除回溯的途径:

.改写文法

对具有多个右部的规则反复提取左因子

1

例1 U∷ =xV|xW

改写为U∷ =x(V|W)

更清楚地表示为:

U∷ =xZ

U, V, W∈Vn, x∈Vt+

注意:问题到此并没有结束,还需要

进一步检查V和W的首符号是否相交

若V∷ =ab|cd FIRST(V) = {a,c}

W∷ =de|fg FIRST(W) = {d,f}

只要不相交就可以根据输入符号确定

目标,若相交,则要代入,并再次提

取左因子。如:V::= ab w::= ac

Z∷ =V|W

则:Z::= a (b|c)

北京航空航天大学计算机学院

2

3

例2:文法G[<程序>]

<

<

<

程序> ∷ = <分程序>| <复合语句>

分程序>∷ = begin<说明串>;<语句串> end

复合语句>∷ = begin<语句串> end

FIRST(<分程序>) = {begin }

FIRST(<复合语句>) = {begin }

改写文法:

<

程序> ∷ = begin (<说明串>;<语句串> end |

语句串> end )

<

引入 <程序*>

<

<

程序> ∷ = begin <程序*>

程序*> ∷ = <说明串>;<语句串> end | <语句串> end

北京航空航天大学计算机学院

2

4

<

<

程序> ∷ = begin <程序*>

程序*> ∷ = <说明串>;<语句串> end | <语句串> end

对于:<程序*>

FIRST( <说明串>;<语句串> end)

=

{real, integer, boolean, array, function, procedure }

FIRST( <语句串>end )

{标识符,goto, begin, if, for}

=

不相交。

北京航空航天大学计算机学院

2

5

2

.超前扫描

当文法不满足避免回溯的条件时,即各选择的首符号相

交时,可以采用超前扫描的方法,即向前侦察各输入符

号串的第二个、第三个符号来确定要选择的目标

这种方法是通过向前多看几个符号来确定所选择的目

标,从本质上来讲也有回溯的味道,因此比第一种方

法费时,但是假读仅仅是向前侦察情况,不作任何语

义处理工作。

北京航空航天大学计算机学院

2

6

例:

<

<

<

程序> ∷ = <分程序>| <复合语句>

分程序>∷ = begin<说明串>;<语句串> end

复合语句>∷ = begin<语句串> end

这两个选择的首符号是相交的,故读到begin时并不能确定

该用哪个选择,这时可采用向前假读进行侦察,此例题只

需假读一次就可以确定目标。

因为<说明串>的首符集为{real,integer,……,procedure}

而<语句串>的首符集为{标识符,if ,for,……,begin}

∴只要超前假读得到的是“说明”的首符,便是第一个选择;

若是“语句”的首符,就是第二个选择。

北京航空航天大学计算机学院

2

7

文法的两个条件

为了在不采取超前扫描的前提下实现不带回溯的自顶向

下分析,文法需要满足两个条件:

1、文法是非左递归的;

2、 对文法的任一非终结符,若其规则右部有多个选择时, 各选择

所推出的终结符号串的首符号集合要两两不相交。

[

定义] 设文法G(不具有左递归性),UVn

U::=  |  | 

1

2

3

*

FIRST(αi) = {a | αi  a…, a Vt }

为避免回溯,对文法的要求是:

FIRST(αi) ∩ FIRST(αj)=φ (ij)

在上述条件下,就可以根据文法构造有效的、不带回溯的

自顶向下分析器。

北京航空航天大学计算机学院

2

8

4

.2.3 递归子程序法(递归下降分析法)

具体做法:对语法的每一个非终结符都编一个分析程序,

当根据文法和当时的输入符号预测到要用某个非终结符

去匹配输入串时,就调用该非终结符的分析程序。

下面通过举例说明如何根据文法构造该文法的

语法分析程序

北京航空航天大学计算机学院

2

9

如文法G[Z]: Z ::= UV

U ::= ….

V ::= ….

Z的分析程序

U的分析程序

V的分析程序

U

V

注:消除左递归后,可有其它递归:

U ::= …U…

U ::= …W…

W::= …U…

北京航空航天大学计算机学院

3

0

例:文法G[Z]

Z∷ =‘(’U‘)’|aUb

U∷ =dZ|Ud|e

1

.检查并改写文法

改写后无左递归且首符集不相交:

Z∷ =‘ (’U‘) ’|aUb

U∷ =(dZ|e){d}

{

{

(}∩{a}=φ

d}∩{e}=φ

2

.检查文法的递归性

+

Z¨·U¨·¨·Z¨·

U¨·Z¨·¨·U¨·

∴Z¨·Z¨·

+

∴U¨·U¨·

因此,Z和U的分析程序要编成递归子程序

北京航空航天大学计算机学院

3

1

3

.算法框图

非终结符号的分析子程序的功能是:

用规则右部符号串去匹配输入串。

以下是以框图形式给出的两个子程序:

北京航空航天大学计算机学院

3

2

入口

F

F

F

error

(sym)=‘(’

(sym) =‘a’

Y

T

读符号

读符号

U

U

F

error

(sym) =‘)’

T

(sym) =‘b’

T

读符号

出口

北京航空航天大学计算机学院

3

3

入口

F

F

error

(sym)=‘d’

(sym) =‘e’

Y

T

读符号

读符号

Z

(sym) =‘d’

T

F

读符号

出口

北京航空航天大学计算机学院

3

4

说明

要注意子程序之间的接口,在程序编制时进入某个非终结

符的分析程序时其所要分析的语法成分的第一个符号已

读入sym中。

递归子程序法对应的是最左推导过程

北京航空航天大学计算机学院

3

5

4

.2.4 用递归子程序法构造语法分析程序的例子

文法: <语句>∷ = <变量>∶ =<表达式>

IF <表达式> THEN <语句>

IF <表达式> THEN <语句> ELSE <语句>

|

|

<

<

<

<

变量>∷ = i | i ‘[’<表达式>‘]’

表达式>∷ = <项>| <表达式> + <项>

项>∷ = <因子>| <项>* <因子>

因子>∷ = <变量>| ‘(’<表达式>‘)’

改写文法: <语句>∷ = <变量>∶ =<表达式>

|

IF<表达式>THEN<语句>[ELSE <语句>]

<

<

<

<

变量>∷ = i[‘[’<表达式>‘]’]

表达式>∷ = <项>{+<项>}

项>∷ = <因子>{*<因子>}

因子>∷ = <变量>|‘(’<表达式>‘)’

北京航空航天大学计算机学院

3

6

语法分析程序所要调用的子程序:

nextsym: 词法分析程序, 每调用一次读进一个单词,

单词的类别码放在sym中。

error:

出错处理程序。

北京航空航天大学计算机学院

3

7

PROCEDURE state;

IF sym =‘IF’ THEN

/*语句分析子程序*/

BEGIN nextsym; expr;

IF sym≠‘THEN’ THEN error

ELSE BEGIN nextsym; state;

IF sym= ‘ELSE’

THEN BEGIN

nextsym;

state;

END

END

END

ELSE BEGIN var;

IF sym≠‘∶ =’

THEN error

ELSE BEGIN

nextsym;

expr;

END

END

北京航空航天大学计算机学院

3

8

PROCEDURE var;

IF sym ≠‘i’ THEN error

ELSE BEGIN nextsym;

IF sym=‘[’ THEN

BEGIN nextsym;

expr;

/*变量*/

IF sym ≠‘]’

THEN error

ELSE nextsym;

END

END

北京航空航天大学计算机学院

3

9

<语句>∷ = <变量>∶ =<表达式>

|

IF<表达式>THEN<语句>[ELSE <语句>]

<

<

<

<

变量>∷ =i[‘[’<表达式>‘]’]

表达式>∷ =<项>{+<项>}

项>∷ =<因子>{*<因子>}

因子>∷ =<变量>|‘(’<表达式>‘)’

PROCEDURE expr;

BEGIN term;

/*表达式*/

WHILE sym=‘+’ DO

BEGIN nextsym;

term;

END

END;

北京航空航天大学计算机学院

4

0

PROCEDURE term;

BEGIN factor;

/*项*/

WHILE sym=‘*’ DO

BEGIN nextsym; factor END

END;

PROCEDURE factor;

BEGIN

/*因子*/

IF sym=‘(’ THEN

BEGIN nextsym; expr;

IF sym ≠‘)’

THEN error

ELSE nextsym

END

ELSE var;

END

北京航空航天大学计算机学院

4

1

4

.2.5 LL分析法

LL-自左向右扫描、自左向右地分析和匹配输入串。

分析过程表现为最左推导的性质。

1

、LL分析程序构造及分析过程

输入串

#

由三部分组成:

分析表

执行程序

符号栈

#

执行程序 (总控程序)

符号栈 (分析栈)

分析表

北京航空航天大学计算机学院

4

2

在实际语言中,每一种语法成分都有确定的左右界

符,为了研究问题方便,统一以‘#’表示。

1)、分析表:二维矩阵M

A∷ =αi

αi∈V*

A ∈Vn

M[A,a]= 或

error

a ∈ Vt or #

北京航空航天大学计算机学院

4

3

M[A,a] = A∷ =αi

表示当要用A去匹配输入串时,且当前输

入符号为a时,可用A的第i个选择去匹配。

*

即 当αi≠ε时,有αi a…;

当αi=ε时,则a为A的后继符号。

M[A,a] = error

表示当用A去匹配输入串时,若当前输入符

号为a,则不能匹配,表示无Aa…,或a不是A的

*

后继符号。

北京航空航天大学计算机学院

4

4

2)符号栈: 有四种情况

开始状态

xxxxxx#

符号栈

#

E

E#

a

工作状态

X

i

X::=

a……#

查分析表得:

X ∈Vn, M[X,a] = X∷ =αi

X+ a…

符号栈

#

…..X

X….#

X ∈Vt, X = a

北京航空航天大学计算机学院

4

5

a

出错状态

X error

a……#

查分析表得:

X ∈Vn, M[X,a] = error

无X+ a…

符号栈

#

…..X

X….#

X ∈Vt, X  a

结束状态

#

符号栈

#

#

北京航空航天大学计算机学院

4

6

a……#

符号栈

#

…..X

3)执行程序

X….#

执行程序主要实现如下操作:

1

2

. 把#和文法识别符号E推进栈, 读入下一个符号,

重复下述过程直到正常结束或出错。

. 测定栈顶符号X和当前输入符号a,执行如下操作:

(1) 若X=a=#,分析成功,停止。E匹配输入串成功。

(2) 若X=a≠#,把X推出栈,再读入下一个符号。

(3) 若X∈Vn,查分析表M。

北京航空航天大学计算机学院

4

7

(3) 若X∈Vn,查分析表M

a) M[X,a] = X∷ = UVW

#

…..X

#…..WVU

UVW….#

则将X弹出栈,将UVW压入

注:U在栈顶 (最左推导)

X….#

b) M[X, a] = error 转出错处理

c) M[X, a] = X::=ε,

a

a为X的后继符号

X

X::=UVW

则将X弹出栈 (不读下一符号)

继续分析。

北京航空航天大学计算机学院

4

8

例:文法G[E]

E ::= E +T | T

消除左递归

E ::= TE’

T ::= T * F | F

F ::= (E)|i

E ::= +TE’|ε

T ::= FT’

T’ ::= *FT’|ε

F ::= (E)|i

北京航空航天大学计算机学院

4

9

分析表

i

+

*

(

)

#

E E ::= T E '

E ::=

T E '

E '

E '::=

E '::=ε E '::=ε

T '::=ε T '::=ε

+

T E '

T T::= F T '

T '

T::=

FT '

T '::= T '::=

FT '

*

ε

F

F ::=

F ::= i

(E )

注:矩阵元素空白表示Error

北京航空航天大学计算机学院

5

0

输入串为: i+i*i#

步骤

符号栈

#E

读入符号

剩余符号串

+i*i#

使用规则

1

2

3

4

5

6

7

.

.

.

.

.

.

.

E#

i

i

#E’T

TE’#

+i*i#

E::=TE’

T::=FT’

F::= i

#E’T’F FT’E’#

#E’T’ i iT’E’#

#E’T’ T’E’#

i

+i*i#

i

+i*i#

+

i*i#

i*i#

i*i#

(出栈,读下一个符号)

T::= ε

#E’

E’#

#E’T+ +TE’#

E’::=+TE’

北京航空航天大学计算机学院

5

1

步骤

符号栈

#E’T

读入符号

剩余符号串

使用规则

8

9

1

.

i

*i#

*i#

*i#

i#

.

#E’T’F

#E’T’ i

#E’T’

#E’T’F*

#E’T’F

#E’T’ i

#E’T’

#E’

i

T::=FT’

F::= i

0.

i

11.

*

*

i

1

1

1

1

1

1

2.

3.

4.

5.

6.

7.

i#

T’::=*FT’

F::= i

#

i

#

#

#

#

T’::= ε

E’::= ε

北京航空航天大学计算机学院

5

2

推导过程:

E  TE'  FT'E'  iT'E' iE'

i+TE'  i+FT'E'  i+iT'E'

i+i*FT'E'  i+i*iT'E'

i+i*iE'  i+i*i

最左推导。

北京航空航天大学计算机学院

5

3

2

、分析表的构造

设有文法G[Z]:

*

定义:FIRST(α)={a| αa…,a∈Vt}

α∈V* , 若αε,则ε∈FIRST(α)

*

该集合称为α的头符号集合。

*

定义:FOLLOW(A)={a| Z…Aa…,a∈Vt}

A∈Vn , Z识别符号

该集合称为A的后继符号集合。

*

特殊地:若Z ...A 则 #∈FOLLOW(A)

北京航空航天大学计算机学院

5

4

构造集合FIRST的算法

设α=X X ...X ,

X ∈V V

t

1

2

n

i

n

求FIRST(α)=?

首先求出组成α的每一个符号Xi的FIRST集合

(1) 若Xi∈Vt,则 FIRST(Xi) = {Xi}

(2) 若Xi ∈Vn 且 Xi∷ =a…..|ε, a ∈Vt

则 FIRST(Xi)={a,ε}

北京航空航天大学计算机学院

5

5

(3) 若Xi ∈Vn 且Xi∷ =y1 y2 …yk,则按如下顺序计算FIRST(Xi)

FIRST(Xi)  FIRST(y1) –{ε};

若ε∈FIRST(y1) 则将FIRST(y2)-{ε}加入FIRST(Xi);

若ε∈FIRST(y1)

ε∈FIRST(y2) 则将FIRST(y3)-{ε}加入FIRST(Xi)

.

.......

若ε∈FIRST(yk-1) 则将FIRST(yk)-{ε}加入FIRST(Xi)

若ε ∈FIRST(y1) ~ FIRST(yk)

则将ε加入FIRST(Xi)

注意:要顺序往下做,一旦不满足条件,过程就要中断进行

得到FIRST(Xi),即可求出FIRST(α)

北京航空航天大学计算机学院

5

6

2

.构造集合FOLLOW的算法

设S, A, B∈Vn ,

算法:连续使用以下规则,直至FOLLOW集合不再扩大

(1) 若S为识别符号,则把“#”加入FOLLOW(S)中

(2) 若A∷ =αBβ (β≠ε),则把FIRST(β)-{ε}加入FOLLOW(B)

*

(3) 若A∷ =αB 或A∷ =αBβ, 且βε则把FOLLOW(A)加

入FOLLOW(B)

注:FOLLOW集合中不能有ε

北京航空航天大学计算机学院

5

7

2

、构造分析表

基本思想是:

终结符号

当文法中某一非终结符

呈现在栈顶时,根据当前

的输入符号,分析表应指

示要用该非终结符的哪

一条规则去匹配输入串

(即进行一步最左推导)

根据这个思想, 不难把构造分析表算法构造出来!

北京航空航天大学计算机学院

5

8

算法:

设A∷ =αi为文法中的任意一条规则,a为任一终结符或#。

1

、若a ∈FIRST(αi ),则A::= αi ==> M[A,a]

表示:A在栈顶,输入符号是a,应选择αi 去匹配

、若αi =ε或αi + ε,而且a ∈FOLLOW(A),

则A::= αi ==>M[A,a],表示A已经匹配输入串成功,

其后继符号终结符a由A后面的语法成分去匹配。

、把所有无定义的M[A,a]都标上error

2

3

北京航空航天大学计算机学院

5

9

例:G[E]分析表的构造

E∷ =TE’

E’∷ =+TE’|ε

T∷ =FT’

求FIRST :

FIRST(F)={(,i}

T’∷ =*FT’|ε

F∷ =(E)|i

FIRST(T’)={*,ε}

FIRST(T)=FIRST(F)-{ε}={(,i}

FIRST(E’)={+,ε}

FIRST(E)= FIRST(T)-{ε}={(,i}

FIRST(TE’)=FIRST(T)-{ε}={(,i}

FIRST(+TE’)={+}

FIRST(ε)={ε}

FIRST(FT’)= FIRST(F)-{ε}={(,i}

FIRST(*FT’)={*}

FIRST((E))={(}

FIRST(ε)={ε}

FIRST(i)={i}

北京航空航天大学计算机学院

6

0

E∷ =TE’

E’∷ =+TE’|ε

T∷ =FT’

T’∷ =*FT’|ε

F∷ =(E)|i

求FOLLOW

FOLLOW(E)={#, )} ∵因为E是识别符号∴#∈FOLLOW(E)

又F∷ =(E) ∴ )∈FOLLOW(E)

FOLLOW(E’)={#, )} ∵E∷ =TE’ ∴FOLLOW(E)加入

FOLLOW(E’)

FOLLOW(T)={+, ), #} ∵E’∷ =+TE’ ∴FIRST(E’)-{ε}加入FOLLOW(T)

又E’ε,∴ FOLLOW(E’)加入FOLLOW(T)

FOLLOW(T’)= FOLLOW(T)= {+,),#}

∵T∷ =FT’ ∴ FOLLOW(T)加入FOLLOW(T’)

FOLLOW(F)={*, + , ), #} ∵T∷ =FT’ ∴ FOLLOW(F)=FIRST(T’)-{ε}

*

又T’ε∴ FOLLOW(T)加入FOLLOW(F)

北京航空航天大学计算机学院

6

1

构造分析表

i

+

*

(

)

#

E E::=TE’

E

E::=TE’

E’∷ =+TE’

E’∷ =εE’∷ =ε

T’∷ =εT’∷ =ε

T T::=FT’

T::=FT’

T

T’∷ =ε T’∷ =*FT’

F F∷ =i

F∷ =(E)

注意:用上述算法可以构造出任意给定文法的分析表,但不是所有

文法都能构造出上述那种形状的分析表即M[A,a]=一条的规则或

Error。对于能用上述算法构造分析表的文法称为LL(1)文法

北京航空航天大学计算机学院

6

2

3

、LL(1)文法

定义:一个文法G,其分析表M不含多重定义入口(即分

析表中无二条以上规则),则称它是一个LL(1)文法.

定理:文法G是LL(1)文法的充分必要条件是:对于G的

每一个非终结符A的任意两条规则A::=α|β,下列条件成立:

1

2

、FIRST(α) ∩FIRST(β) = Ф

*

、若β==> ε, 则FIRST(α) ∩FOLLOW(A) = Ф

北京航空航天大学计算机学院

6

3

用此构造分析表的算法,可以构造任何文法的分析表,

但对于某些文法,有些M[A,a]中可能有若干条规则,这称

为分析表的多重定义或者多重入口。

可以证明: 如果G是左递归的,或者是二义性的文法,则至少有一

个多重入口。

U∷ =U…| a …

则有:FIRST(U…)∩FIRST(a…)≠φ

左递归:

M[U,a]={U∷ =U…, U∷ =a…}

对文法所定义的某些句子存在着两个最左推导,

即在推导的某些步上存在多重定义,有两条规则

可用,所以分析表是多重定义的。

二义文法:

北京航空航天大学计算机学院

6

4

4

、LL分析的错误恢复----补充(不要求)

当符号栈顶的终结符和下一个输入符号不匹配,或栈

顶是非终结符A, 输入符号a,而M[A,a]为空白(即error)时,

分析发现错误。

错误恢复的基本思想是: 跳过一些输入符号,直到期望

的同步符号之一出现为止。

同步符号(可重新开始继续分析的输入符号)集合通

常可按以下方法确定:

北京航空航天大学计算机学院

6

5

1

2

) 把FOLLOW(A)的所有符号加入A的同步符号集合,跳过输

入符号直到出现FOLLOW(A)的元素, 便把A从栈中弹出,

继续往下分析。

) 为了避免仅按1)来确定同步符号集合会使跳读过多(如输入

串中缺少语句结束符号“;”),可将程序高层语法结构(成分)的

开始符号(通常是关键词)加入到低层语法结构的同步集合中。

3

4

5

) 把FOLLOW(A)的符号加入A的同步集合中。

) 如果栈顶的非终结符号A可以产生空串,可以将A从栈中弹出。

) 如果终结符在栈顶而不能匹配,则可弹出该终结符,继续分析,

这好比把所有其他符号均作为该符号的同步集合元素。

北京航空航天大学计算机学院

6

6

4.3 自底向上分析

基本算法思想:

若采用自左向右的描述和分析输入串,那么自

底向上的基本算法是:

从输入符号串开始,通过重复查找当前句型的句

柄(最左简单短语),并利用有关规则进行归约,若能

归约为文法的识别符号,则表示分析成功,输入符号

串是文法的合法句子,否则有语法错误。

北京航空航天大学计算机学院

6

7

分析过程是重复以下步骤:

1

、找出当前句型的句柄 x (或句柄的变形)

2

3

、找出以 x 为右部的规则 X::= x

、把 x 归约为X,产生语法树的一枝

关键:找出当前句型的句柄 x (或其变形),这不是很容易。

北京航空航天大学计算机学院

6

8

主要内容:

自底向上分析的一般过程(移进-归约分析)

算符优先分析法

LR分析法

北京航空航天大学计算机学院

6

9

4

.3.1 移进—归约分析(Shift-reduce parsing)

要点:建立符号栈,用来记录分析的历史和现状,

并根据所面临的状态,确定下一步动作是移

进还是归约。

输入串

#

符号栈

S.R.P

#

北京航空航天大学计算机学院

7

0

分析过程:把输入符号串按扫描顺序一一地移进

符号栈(一次移一个),检查栈中符号,当在栈顶的若

干符号形成当前句型的句柄时,就根据规则进行归约,

将句柄从符号栈中弹出,并将相应的非终结符号压入

栈内(即规则的左部符号),然后再检查栈内符号串是

否形成新的句柄,若有就再进行归约,否则移进符号。

分析一直进行到读到输入串的右界符为止。最后,若栈

中仅含有左界符号和识别符号,则表示分析成功,否则

失败。

北京航空航天大学计算机学院

7

1

例:G[S]:

S ∷ = aAcBe

A∷ = b

输入串为:

abbcde

A∷ = Ab

B∷ = d

输入串为abbcde,检查是否是该文法的合法句子:

若采用自底向上分析,即能否一步步归约当前句型的

句柄 ,最终归约到识别符号S。先设立一个符号栈,将

符号“#”作为待分析的符号串的左右分界符。

作为初始状态,先将符号串的左分界符推进符号栈,作

为栈底符号。

分析过程如下表:

北京航空航天大学计算机学院

7

2

步骤

符号栈

输入符号串

动作

1

#

abbcde#

准备,初始化

2

3

4

5

6

7

8

9

#a

#ab

bbcde#

bcde#

bcde#

cde#

cde#

de#

e#

移进

移进

#aA

归约(A∷ =b)

移进

#aAb

#aA

归约(A∷ =Ab)

移进

#aAc

#aAcd

#aAcB

#aAcBe

#S

移进

e#

归约(B∷ =d)

移进

10

#

11

#

归约(S∷ =aAcBe)

成功

12

#S

#

北京航空航天大学计算机学院

7

3

这一方法简单明了,不断地进行移进归约,关键是确定

当前句型的句柄。

说明: 1) 例子的分析过程是一步步地归约当前句型的句柄

S

该句子的唯一语法树为:

A

B

A

a b b c d e

北京航空航天大学计算机学院

7

4

注意两点:

1)栈内符号串 + 未处理输入符号串 = 当前句型

2)句柄都在栈顶

实际上,以上分析过程并未真正解决句柄的识别问题

北京航空航天大学计算机学院

7

5

2

) 未真正解决句柄的识别。

上述分析过程是怎样识别句柄的,主要看栈顶符号串

是否形成规则的右部。

这种做法形式上是正确的,但在实际上不一定正确。

举例的分析过程可以说是一种巧合。

因为不能认为: 对句型 xuy 而言

若有U∷ = u,即Uu 就断定u是简单短语,

*

u 就是句柄,而是要同时满足 Z xUy

北京航空航天大学计算机学院

7

6

4

.3.2 算符优先分析(Operator-Precedence Parsing)

1

) 这是一种经典的自底向上分析法,简单直观,并被广泛使

用,开始主要是对表达式的分析,现在已不限于此。可以

用于一大类上下无关的文法。

2

) 称为算符优先分析是因为这种方法是仿效算术式的四则运算

而建立起来的,作算术式的四则运算时,为了保证计算结果

和过程的唯一性,规定了一个统一的四则运算法则,规定运

算符之间的优先关系。

1

2

3

.乘除的优先级大于加减

.同优先级的运算符左大于右

.括号内的优先级大于括号外

运算法则:

于是: 4+8-6/2*3 运算过程和结果唯一

北京航空航天大学计算机学院

7

7

3

) 算符优先分析的特点:

仿效四则运算过程,预先规定相邻终结符之间的优

先关系,然后利用这种优先关系来确定句型的“句柄” ,

并进行归约。

4

) 分析器结构:

输入串

#

分析程序

符号栈

#

优先关系矩阵

北京航空航天大学计算机学院

7

8

例: G[E]

E∷ =E+E | E*E | (E) | i

Vt={+, *, (, ), i}

这是一个二义文法,要用算符优先法分析由该文法所确定的

语言句子, 如:i+i*i

(1) 先确定终结符之间的优先关系

优先关系的定义:

设 a, b 为可能相邻的终结符

定义: a =. b

a的优先级等于b

a的优先级小于b

a的优先级大于b

.

a< b

.

a > b

北京航空航天大学计算机学院

7

9

1

) 例中文法终结符之间的优先关系可以用一个矩阵M来表示

b(右,栈外)

+

*

i

(

)

#

a(左,栈内)

.

. . . . .

+

>

<

<

<

>

>

.

. . . . .

*

i

>

>

<

<

>

>

.

.

.> .

>

>

>

.

. . . .

(

<

<

<

<

=

.

.

.> .

)

>

>

>

.

. . .

#

<

<

<

<

北京航空航天大学计算机学院

8

0

2

) 矩阵元素空白处表示这两个终结符不能相邻,故没有

优先关系

(2) 分析过程

算法:

i+i*i

当栈顶项(或次栈顶项)终结符的优先级大于

栈外的终结符的优先级,则进行归约,否则

移进。

北京航空航天大学计算机学院

8

1

E∷ =E+E | E*E | (E) | i

步骤

符号栈

输入串

i+i*i#

+i*i#

+i*i#

i*i#

优先关系

#<i

动作

移进

归约

移进

移进

归约

1

#

2

3

4

5

#i

i>+

#E

#<+

#E+

#E+i

+<i

*i#

i>*

6

7

8

#E+E

#E+E*

#E+E*i

*i#

i#

#

+<*

*<i

i>#

移进

移进

归约

9

#E+E*E

#E+E

#E

#

#

#

*>#

+>#

归约

归约

接受

10

11

分析过程是从符号串开始,根据相邻终结符之间的优先

关系确定句型的“句柄”,并进行归约,直到识别符号E,最

后分析成功: i+i*i∈L(G[E])

出错情况:

1

2

3

. 相邻终结符之间无优先关系

. 对双目运行符进行归约时,符号栈中无足够项

. 非正常结束状态

北京航空航天大学计算机学院

8

3

重要说明

(1) 上述分析过程不一定是严格的最左归约(即不一定是

规范归约)也就是每次归约不一定是归约当前句型的

句柄,而是句柄的变形,但也是短语。

(2) 文法的终结符优先关系可以用一个矩阵表示,也可以用

两个优先函数来表示:

f—栈内优先函数

g—栈外优先函数

若 a < b 则令 f(a) < g(b)

.

.

.

a = b

a > b

f(a) = g(b)

f(a) > g(b)

北京航空航天大学计算机学院

8

4

根据这些原则,构造出上述文法的优先函数:

算符优先函数值的确定方法

1

2

. 把各算符优先级由小到大定为j=0~n

#

( + * ) i

0

0 1 2 3

. 对于各算符的优先顺序

若为左结合,则 f (op)=2j g (op)=2j-1

若为右结合,则 f (op)=2j g (op)=2j

设m>2n, 则 f (j) = f (i) = m+1

g (j) = g (i) = m,其他为0

f(#) = f(() = g(#) = g()) = 0

北京航空航天大学计算机学院

8

5

+

*

(

) i

0 5 5 0

6 0 6 0

#

f(栈内) 2 4

g(栈外) 1 3

f(+) > g(+)

f(+) < g(*)

f(+) < g(()

左结合

先乘后加

先括号内后括号外

北京航空航天大学计算机学院

8

6

特点:

(1) 优先函数值不唯一

(2) 优点:

节省内存空间

若文法有n个终结符,则关系矩阵为n2

而优先函数为2n

易于比较:算法上容易实现,数与数比,不必

查矩阵。

(3) 缺点:可能掩盖错误。

北京航空航天大学计算机学院

8

7

(3) 可以设立两个栈来代替一个栈

运算对象栈(OPND)

运算符栈(OPTR)

优点:便于比较,只需将输入符号与运算符栈的

栈顶符号相比较

(4) 使用算符优先分析方法可以分析二义性文法所产生的语言

二义性文法按规范分析,其句柄不唯一

例: G[E]

这是一个二义性文法,

i+i*i有两棵语法树

E∷ =E+E|E*E|(E)|i

Vt={+, *, (, ), i}

北京航空航天大学计算机学院

8

8

E

E

5

5

E

+

E

E

*

E

4

3

1

4

E * E

E + E

i

i

2

3

1

2

i

i

i

i

按规范归约,句柄不唯一, E+E*i 所以整个归约过程就

不唯一,编译所得的结果也将不唯一。

北京航空航天大学计算机学院

8

9

4

.3.3 算符优先分析法的进一步讨论

三个问题:

(1) 算符优先文法(OPG)

(2) 构造优先关系矩阵

(3) 算符优先分析算法的设计

北京航空航天大学计算机学院

9

0

1)算符优先文法(OPG-Operator Precedence Grammar)

算符文法(OG)的定义

若文法中无形如U∷ = ·¨VW·¨的规则,这里V,W∈Vn

则称G为OG文法,也就是算符文法。

优先关系的定义

若G是一OG文法,a,b∈Vt , U,V,W∈Vn

分别有以下三种情况:

北京航空航天大学计算机学院

9

1

.

1

2

3

) a=b iff 文法中有形如 U∷ = ·¨ab·¨或U∷ = ·¨aVb·¨

的规则。

.

) a<b iff 文法中有形如 U∷ =·¨aW·¨的规则,其中

W b·¨或W Vb·¨ 。

+

+

.

) a>b iff 文法中有形如 U∷ =·¨Wb·¨的规则, 其中

+

+

W ·¨a或W ·¨aV 。

北京航空航天大学计算机学院

9

2

例:文法G[E]

E::= E + T | T

T::= T * F | F

F::= ( E ) | i

E::=E + T

E => E + T ∴ + >· +

T => T * F ∴ + <· *

T =>F =>( E ) ∴ + (

<

·

T =>F =>i ∴ + i

<

·

F::= ( E )

E => E + T ∴ + ·> )

( )

( <· +

=

·

北京航空航天大学计算机学院

9

3

算符优先文法(OPG)的定义

设有一OG文法,如果在任意两个终结符之间,至多只有

上述关系中的一种,则称该文法为算符优先文法(OPG)

对于OG文法的几点说明:

(1) 运算是以中缀形式出现的

(2) 可以证明,若文法为OG文法,则不会出现两个非

终结符相邻的句型。

(3) 算法语言中的表达式以及大部分语言成分的文法均

是OG文法

北京航空航天大学计算机学院

9

4

2)构造优先关系矩阵

.

求 “ = ” 检查每一条规则,若有U::=…ab…或

.

U::=…aVb…, 则 a=b

.

.

求“ < ”、“ > ” ,需定义两个集合

+

+

FIRSTVT( U ) = {b|Ub…或UVb…,b∈Vt, V∈Vn}

+

+

LASTVT( U ) = {a|U…a或U…aV,a∈Vt, V∈Vn}

北京航空航天大学计算机学院

9

5

.

.

求“ < ”、“ > ”:

若文法有规则

W∷ =...aU... ,对任何b, b∈FIRSTVT(U)

.

则有:a < b

若文法有规则

W∷ =...Ub... ,对任何a, a∈LASTVT(U)

.

则有:a > b

北京航空航天大学计算机学院

9

6

构造FIRSTVT(U)的算法

+

+

1

) 若有规则U∷ = b…或U∷ = Vb…(存在Ub…或UVb…)

则b∈FIRSTVT(U)

2

) 若有规则U∷ = V…且b∈FIRSTVT(V), 则b∈FIRSTVT(U)

+

+

+

说明:因为Vb…或VWb…, 所以有UV…b…或

UV… + Wb…

北京航空航天大学计算机学院

9

7

具体方法如下:

设一个栈S和一个二维布尔数组F

F[U,b]=TRUE iff b∈FIRSTVT(U)

PROCEDURE INSERT(U,b)

IF NOT F[U,b] THEN

BEGIN

F[U,b]:=TRUE;

把(U,b)推进S栈 /* b∈FIRSTVT(U) */

END

BEGIN {main}

FOR 每个非终结符号U和终结符b DO

F[U,b]:=FALSE;

FOR 每个形如U::=b…或U::=Vb… 的规则 DO

INSERT(U,b);

北京航空航天大学计算机学院

9

8

WHILE S栈非空 DO

BEGIN

把S栈的栈顶项弹出,记为 (V,b) /* b∈FIRSTVT(V)*/

FOR 每条形如U::=V…的规则 DO

INSERT (U,b); /* b∈FIRSTVT(U)*/

END OF WHILE

END

上述算法的工作结果是得到一个二维的布尔数组F,从F

可以得到任何非终结符号U的FIRSTVT

FIRSTVT(U) = { b | F[U,b] = TRUE }

北京航空航天大学计算机学院

9

9

构造LASTVT(U)的算法

1

2

. 若有规则U::=…a或U::=…aV,则a∈LASTVT(U)

. 若有规则U::=…V,且a∈LASTVT(V), 则a∈LASTVT(U)

设一个栈ST,和一个布尔数组B

PROCEDURE INSERT(U,a)

IF NOT B[U,a] THEN

BEGIN

B[U,a]::=TRUE; 把(U,a)推进ST栈;

END;

北京航空航天大学计算机学院

1

00

BEGIN

FOR 每个非终结符号U和终结符号a DO

B[U,a]:=FALSE;

FOR 每个形如U::=…a或U::=…aV的规则 DO

INSERT (U,a);

WHILE ST栈非空 DO

BEGIN

把ST栈的栈顶弹出,记为(V,a);

FOR 每条形如U::=…V的规则 DO

INSERT(U,a);

END OF WHILE;

END;

北京航空航天大学计算机学院

1

01

构造优先关系矩阵的算法

FOR 每条规则U::=x1x2…xn DO

FOR i:=1 TO n-1 DO

BEGIN

IF xi和xi+1均为终结符,THEN 置 xi=. xi+1

IF i≤n-2,且xi和xi+2都为终结符号但

xi+1为非终结符号 THEN 置 xi=.xi+2

IF xi为终结符号,xi+1为非终结符号 THEN

FOR FIRSTVT(xi+1)中的每个b DO

.

置xi<b

IF xi为非终结符号,xi+1为终结符号 THEN

FOR LASTVT(xi)中的每个a DO

置a>. xi+1

END

北京航空航天大学计算机学院

1

02

3)算符优先分析算法的实现

先定义优先级,在分析过程中通过比较相邻运算符之

间的优先级来确定句型的“句柄”并进行归约。

[

定义] 素短语:文法G的句型的素短语是一个短语,

它至少包含有一个终结符号,并且除它自身以外不再

包含其他素短语。

北京航空航天大学计算机学院

1

03

例: 文法G[E]

E::=E+T|T

文法的语法树:

T::=T*F|F

F::=(E)|i

求句型T+T*F+i 的素短语

E

+

E

T

F

短语:T+T*F+i, T+T*F

T(最左), T*F, i

E + T

其中T不包含终结符,T是句柄

而T+T*F+i和T+T*F包含其他

素短语。

i

T T * F

只有T*F和i为素短语,其中T*F为最左素短语,而该句型句

柄为T。

北京航空航天大学计算机学院

1

04

句型:T + T + i

短语:T+T+i

T +T

T

i

句柄:T

素短语:T+T, i

北京航空航天大学计算机学院

1

05

算符优先分析法如何确定当前句型的最左素短语?

设有OPG文法句型为:

#

N1a1N2a2…NnanNn+1#

其中Ni为非终结符(可以为空), ai为终结符

定理:一个OPG句型的最左素短语是满足下列条件的

最左子串:aj-1Njaj…NiaiNi+1ai+1

其中 aj-1<. aj

.

.

.

.

aj=aj+1, aj+1= aj+2 ,…, ai-2= ai-1, ai-1= ai

.

ai> ai+1

北京航空航天大学计算机学院

1

06

根据该定理,要找句型的最左素短语就是要找满足

上述条件的最左子串。

Njaj…NiaiNi+1

注意:出现在aj左端和ai右端的非终结符号一定

属于这个素短语,因为运算是中缀形式给

出的(OPG文法的特点)NaNaNaN NaWaN

例: 文法G[E]

E::=E+T|T

T::=T*F|F

F::=(E)|i

分析文法的句型T+T*F+i

北京航空航天大学计算机学院

1

07

步骤

句型

关系

. . . .

. . .

最左子串 归约符号

.

1

2

3

4

#T+T*F+i# #<+<*>+<i># T*F

T

E

F

E

.

#T+T+i#

#E+i#

#<+>+<i># T+T

.

. .

#<+<i>#

i

.

.

#E+F#

#<+>#

E+F

可以看出:

1

2

3

. 每次归约最左子串,确实是当前句型的

最左素短语(语法树)

. 归约的不都是真句柄(仅i归约为F是句柄,

但它是最左素短语)

. 没有完全按规则进行归约,因为素短语不一

定是简单短语

北京航空航天大学计算机学院

1

08

E

E

E

+

T

F

+

E

T

F

E + T

E

+

T

T * F

i

T

i

T

T

.

. . . .

. . . .

#

<+<*>+<i>#

#<+>+<i>#

E

E

+

T

F

E

+

T

E

FF

.

.

.

. .

i

#<+>#

#

<+<i>#

北京航空航天大学计算机学院

1

09

算符优先分析法的实现:

基本部分是找句型的最左子串(最左素短语)

并进行归约。

输入串

#

分析程序

符号栈

#

优先关系矩阵

当栈内终结符的优先级<=栈外的终结符的优先级时,移

进;栈内终结符的优先级>栈外的终结符的优先级时,表

明找到了素短语的尾,再往前找其头,并进行归约。

北京航空航天大学计算机学院

110

4

.3.4 LR分析法

、概述

1

什么是LR分析:从左到右扫描(L)自底向上进行归约(R)

(是规范归约), 是自底向上分析方法的高度概括和集中

历史 + 展望 + 现状 => 句柄

(1) LR分析法的优缺点:

1

2

3

4

5

) 适合文法类足够大

) 分析效率高

) 报错及时

) 可以自动生成

) 手工实现工作量大

北京航空航天大学计算机学院

111

(2) LR分析器有三部分: 状态栈 分析表 控制程序

输入串

#

状态栈:放置分析

器状态和文法符号。

状态栈

控制程序

#

分析表

分析表:由两个矩阵组成,其功能是指示分析器的动作,

是移进还是归约,根据不同的文法类要采用不同

的构造方法。

控制程序:执行分析表所规定的动作,对栈进行操作。

北京航空航天大学计算机学院

112

(3) 分析表的种类

a) SLR分析表(简单LR分析表)

构造简单,最易实现,大多数上下文无关文法都

可以构造出SLR分析表,所以具有较高的实用

价值。使用SLR分析表进行语法分析的分析器

叫SLR分析器。

b) LR分析表(规范LR分析表)

适用文法类最大,几乎所有上下文无关文法都能

构造出LR分析表, 但其分析表体积太大,实

用价值不大。

北京航空航天大学计算机学院

113

c) LALR分析表(超前LR分析表)

这种表适用的文法类及其实现上难易在上面

两种之间,在实用上很吸引人。

使用LALR分析表进行语法分析的分析器叫LALR分析器。

例:UNIX----YACC

文法规则文件

YACC源文件

某语言的

LALR分析器

YACC

北京航空航天大学计算机学院

114

(4) 几点说明

1

2

3

. 三种分析表对应三类文法

. 一个SLR文法必定是LALR文法和LR文法

. 仅讨论SLR分析表的构造方法

北京航空航天大学计算机学院

115

2

、LR分析

(1)逻辑结构

输入串:#a a ... a ... a #

1

2

i

n

状态栈

S x S x

分 状

析 态 LR分析器

动 转

作 移

表 表

#

......

x S

m m

0

1 1 2

北京航空航天大学计算机学院

116

S S ...... Sm

#

S x S x

......

x S

m m

0 1

0

1 1 2

#

x1x2..... xm

S0,S1,…,Sm 状态

状态栈:

S0---初始状态

Sm---栈顶状态

栈顶状态概括了从分析开始到该状态的

全部分析历史和展望信息。

符号串: X X ..... X

m

1

2

为从开始状态(S0)到当前状态(Sm)所识别

的规范句型的活前缀。

北京航空航天大学计算机学院

117

规范句型: 通过规范归约得到的句型。

规范句型前缀: 将输入串的剩余部分与其连接起来就

构成了规范句型。

如: x1x2..... x a ... a 为规范句型

m

i

n

活前缀: 若分析过程能够保证栈中符号串均是规范句型

的前缀,则表示输入串已分析过的部分没有语

法错误,所以称为规范句型的活前缀。

北京航空航天大学计算机学院

118

规范句型的活前缀:

对于句型αβt, β表示句柄,如果αβ= u1u2…ur

那么符号串u1u2…ui(1≤i≤r)即是句型αβt的活前

例: 有文法G[E]: E→T|E+T|E-T

T→i|(E)

拓广文法G’[S]: S→E#

E→T|E+T|E-T

T→i|(E)

句型E-(i+i) #

活前缀:E, E-, E-(, E-(i 是句型E-(i+i)# 的活前缀。

北京航空航天大学计算机学院

119

分析表

a. 状态转移表 (GOTO表)

GOTO表

符号

E T F i + * ( ) #

状态

S

S

S

:

0

1

2

是一个矩阵:

行---分析器的状态

列---文法符号

S

n

北京航空航天大学计算机学院

1

20

GOTO表

符号

E T F i + * ( ) #

状态

S

S

S

:

0

1

2

S

n

GOTO[Si-1, xi]=Si

Si-1---当前状态(栈顶状态)

xi--- 新的栈顶符号

Si----新的栈顶状态(状态转移)

#

S x S x x S x S

......

1 1 2 i-1 i-1 i i

0

Si需要满足条件是:

若X1 X2…. Xi-1是由S0到Si-1所识别的规范句型的

活前缀,则X1 X2…. Xi是由S0到Si所识别的规范句型

的活前缀。

北京航空航天大学计算机学院

1

21

通过对有穷自动机的了解, 可以看出:

状态转移函数GOTO是定义了一个以文法符

号集为字母表的有穷自动机,该自动机识别文法所

有规范句型的活前缀。

M=(S, V, GOTO, S0, Z )

xi-1

Si-1

S

2

x

2

x

i

S

1

x

1

S

i

S

0

北京航空航天大学计算机学院

1

22

b. 分析动作表(ACTION表)

ACTION表

输入符号a

状态s

+

*

i

( ) #

S

S

S

:

0

1

2

S

n

ACTION[Si,a] = 分析动作 a∈Vt

北京航空航天大学计算机学院

1

23

分析动作:

(1) 移进(shift)

ACTION[Si,a] = s

动作: 将a推进栈,并设置新的栈顶状态Sj

= GOTO[Si,a],将指针指向下一个

输入符号

S

j

(2) 归约(reduce)

ACTION[Si,a] = rd

d:文法规则编号 (d) A→β

动作: 将符号串β(假定长度为n)连同状态从栈内

弹出, 把A推进栈, 并设置新的栈顶状态Sj

S

j

= GOTO[Si-n,A]

北京航空航天大学计算机学院

1

24

(3) 接受(accept)

ACTION[Si,#]=accept

(4) 出错(error)

ACTION[Si,a]=error

北京航空航天大学计算机学院

1

25

控制程序: (Driver Routine)

1

2

、 根据栈顶状态和现行输入符号,查分析动

作表(ACTION表),执行由分析表所规定

的操作;

、并根据GOTO表设置新的栈顶状态(即实现

状态转移)。

北京航空航天大学计算机学院

1

26

2) LR分析过程

例:文法G[E]

(1) E ::= E+T

(2) E ::= T

(3) T ::= T*F

(4) T ::= F

(5) F ::= (E)

(6) F ::= i

该文法是SLR文法,故可以构

造出SLR分析表(ACTION表

和GOTO表)

北京航空航天大学计算机学院

1

27

GOTO表

文法符号

状态

E

1

T

2

F

i

+

6

*

(

)

0

1

2

3

4

5

6

7

8

9

1

(S0)

(S1)

3

5

4

(S2)

(S3)

(S4)

(S5)

(S6)

(S7)

(S8)

(S9)

0(S10)

7

8

2

9

3

5

4

3

5

5

4

4

10

6

11

7

11(S11)

北京航空航天大学计算机学院

1

28

ACTION 表

GOTO 表

输入符号

状态

北京航空航天大学计算机学院

1

29

分析过程 i*i+i

步骤 状态栈

符号

输入串

i*i+i#

*i+i#

*i+i#

*i+i#

i+i#

动作

初始化

1

2

3

4

5

6

7

#0

#0i5

#i

S

#0F3

#0T2

#0T2*7

#F

#T

#T*

r6

r4

S

#0T2*7i5 #T*i

#0T2*7F10 #T*F

+i#

S

+i#

r6

北京航空航天大学计算机学院

1

30

8

9

#0T2

#T

#E

+i#

+i#

i#

#

r3

#0E1

r2

1

0

#0E1+6

#E+

S

11

#0E1+6i5 #E+i

#0E1+6F3 #E+F

#0E1+6T9 #E+T

S

1

2

3

4

5

#

r6

1

1

1

#

r4

#0E1

#E

#E

#

r1

accept

北京航空航天大学计算机学院

1

31

由分析过程可以看到:

E

8

(1) 每次归约总是归约当前句型的句柄,是

规范归约。

E + T

7

5

(算符优先分析归约最左素短语)

T

F

4

6

i

T * F

(2) 分析的每一步栈内符号串均是规范句

型的活前缀,与输入串的剩余部分构成

规范句型。

2

3

i

F

1

i

北京航空航天大学计算机学院

1

32

3

、构造SLR分析表

构造LR分析器的关键是构造其分析表。

构造LR分析表的方法是:

1)根据文法构造识别规范句型活前缀的有穷

自动机DFA

2)由DFA构造分析表

北京航空航天大学计算机学院

1

33

(1) 构造DFA

DFA 是一个五元式

1

M=(S, V, GOTO, S0, Z)

S:有穷状态集

在此具体情况下,S = LR(0) ,项目集规范族。

项目集规范族:其元素是由项目所构成的集合。

V:文法字汇表

S0:初始状态 S0∈S

北京航空航天大学计算机学院

1

34

GOTO: 状态转移函数

GOTO[Si,X]=Sj

S

i

,Sj∈S

S

i

,Sj为项目集合

X∈Vn∪Vt

表示当前状态Si面临文法符号为X时,应将状态转移到Sj

Z:终态集合 Z = S-{S0}

即除S0以外,其余全部是终态

构造DFA:

一、确定S集合,即LR(0)项目集规范族,

同时确定S0

二、确定状态转移函数GOTO

北京航空航天大学计算机学院

1

35

2

构造LR(0)的方法

LR(0) 是DFA的状态集,其中每个状态又都是项目的集合。

项目:文法G的每个产生式(规则)的右部添加一个圆点

就构成一个项目。

例:产生式:A→XYZ

项 目: A→.XYZ

A→X.YZ

A→XY.Z

A→XYZ.

项目的直观意义: 指明在分

析过程中的某一时刻已经归

约的部分和等待归约部分。

产生式:A→ε

项 目: A→.

北京航空航天大学计算机学院

1

36

构造LR(0)的方法(三步)

) 将文法拓广

1

目的:使构造出来的分析表只有一个接受状态,这是

为了实现的方便。

方法:修改文法,使识别符号的规则只有一条。

G[E]

E→···

E→···

G'[E']

E'→E

E→···

E→···

拓广

·

··

·

··

L(G(E))=L(G'[E'])

北京航空航天大学计算机学院

1

37

2

) 根据文法列出所有的项目

3

) 将有关项目组合成集合,即DFA中的状态;

所有状态再组合成一个集合,即LR(0)项目集规范族

通过一个具体例子来说明LR(0)的构造以及DFA

的构造方法。

例:G[E]

E→E+T | T

T→T*F | F

F→(E) | i

北京航空航天大学计算机学院

1

38

1

2

将文法拓广为G’[E’]

(0) E’→E

(1) E→E+T

(2) E→T

(4) T→F

(5) F→(E)

(6) F→i

(3) T→T*F

列出文法的所有项目

(1) E’→.E

(2) E’→E.

(6) E→E+T. (11)T→T*.F (16)F→(.E)

(7) E→.T (12)T→T*F. (17)F→(E.)

(3) E→.E+T (8) E→T.

(13)T→.F

(18)F→(E).

(19)F→.i

(4) E→E.+T (9) T→.T*F (14)T→F.

(5) E→E+.T (10) T→T.*F (15)F→.(E) (20) F→i.

北京航空航天大学计算机学院

1

39

3

将有关项目组成项目集,所有项目集构成的集合即为LR(0)

为实现这一步,先定义:

项目集闭包closure

状态转移函数GOTO

北京航空航天大学计算机学院

1

40

A.项目集闭包closure的定义和计算:

令I是文法G’的任一项目集合,定义closure(I)为项目

集合I的闭包,可用一个过程来定义并计算closure(I):

Procedure closure(I);

begin

将属于I的项目加入closure(I);

repeat

for closure(I)中的每个项目A→α.Bβ(B∈Vn) do

将B→.r(r∈V* )加入closure(I)

until closure(I)不再增大

end

北京航空航天大学计算机学院

1

41

B. 状态转移函数GOTO的定义:

GOTO(I,X) = closure(J)

I:项目集合

X:文法符号,X∈V

J:项目集合

J = { 任何形如A→αX.β的项目| A→α.Xβ∈I }

closure(J):项目集J的闭包, 仍是项目集合

所以,GOTO(I,X) = closure(J) 的直观意义是:

它规定了识别文法规范句型活前缀的DFA, 从状态

I(项目集)出发,经过X弧所应该到达的状态(项目集合)

北京航空航天大学计算机学院

1

42

例:

I={E’→E. , E→E.+T} 求GOTO(I , + ) =?

GOTO(I , +) = closure(J)

J = {E→E+.T}

GOTO(I , +) = {E→E+.T , T→.T*F , T→.F ,

F→.(E) , F→.i }

北京航空航天大学计算机学院

1

43

LR(0)和GOTO的构造算法:

G'→LR(0), GOTO

Procedure ITEMSETS(G')

begin

LR(0) := {closure({E'→.E})};

repeat

for LR(0)中的每个项目集I和G'的每个符号X do

if GOTO(I , X)非空,且不属于LR(0)

then 把GOTO(I , X)放入LR(0)中

until LR(0)不再增大

end

北京航空航天大学计算机学院

1

44

例:求G'[E']的LR(0)

G'[E']共有20个项目

V={E, T, F, i, +, *, (, )}

LR(0)={I0,I1,I2,…I11} 由12个项目集组成:

I

0

:

E'→.E

E→.E+T

E→.T

closure({E’→.E}) = I0

T→.T*F

T→.F

F→.(E)

F→.i

GOTO(I0,E) = closure({E’→E.

E→E.+T})

I1: E'→E.

E→E.+T

=

I

1

北京航空航天大学计算机学院

1

45

I

2

:

E→T.

GOTO(I0,T) = closure({E→T. T→T.*F}) = I2

T→T.*F

T→F.

F→(.E)

E→.E+T

E→.T

T→.T*F

T→.F

GOTO(I0,F) = closure({T→F.}) = I3

GOTO(I0,() = closure({F→(.E)}) = I4

I

I

3

:

:

4

F→.(E)

F→.i

I5: F→i.

GOTO(I0,i) =closure({F→i.})= I5

GOTO(I0,*) =φ

GOTO(I0,+) =φ

GOTO(I0,)) =φ

北京航空航天大学计算机学院

1

46

I

6

:

E→E+.T

T→.T*F

T→.F

GOTO(I1,+) = closure({E→E+.T}) = I6

GOTO(I1,其他符号)为空

F→.(E)

F→.i

I

7

:

T→T*.F

F→.(E)

F→.i

GOTO(I2,*) = closure({T→T*.F}) = I7

GOTO(I2,其他符号)为空

GOTO(I3,所有符号)为空

北京航空航天大学计算机学院

1

47

I

8

:

F→(E.)

E→E.+T

GOTO(I4,E) = closure({F→(E.),E→E.+T}) = I8

GOTO(I4,T) = I2 ∈LR(0)

GOTO(I4,F) = I3 ∈LR(0)

GOTO(I4,() = I4 ∈LR(0)

GOTO(I4,i) = I5 ∈LR(0)

GOTO(I4,+) =φ

GOTO(I4,*) =φ

GOTO(I4,)) =φ

GOTO(I5,所有符号) =φ

I

9

:

E→E+T.

T→T.*F

GOTO(I6,T) = closure({E→E+T.,T→T.*F}) = I9

GOTO(I6,F) = I3

GOTO(I6,() = I4

GOTO(I6,i) = I5

北京航空航天大学计算机学院

1

48

I10:T→T*F. GOTO(I7,F) = closure({T→T*F .}) = I10

GOTO(I7,() = I4

GOTO(I7,i) = I5

I11:F→(E). GOTO(I8,)) =closure({F→(E) .}) = I11

GOTO(I8,+) = I6

GOTO(I9,*)= I7

GOTO(I10,所有符号) =φ, GOTO(I11,所有符号) =φ

北京航空航天大学计算机学院

1

49

3

构造DFA

M = (S, V, GOTO, S0, Z)

S = {I0, I1, I2, …, I11} = LR(0)

V = {+, *, i, (, ), E, T, F}

GOTO(Im , X) = In

S

0

= I0

Z = S-{I0} = {I1, I2, …, I11}

M的图解表示如下:

北京航空航天大学计算机学院

1

50

+

T

I

I

I

1

5

3

4

2

I

I

6

i

I

9

E

i

+

i

i

*

start

F

F

I

0

(

8

I11

(

F

I

(

)

E

T

I

7

I10

T

I

(

F

*

北京航空航天大学计算机学院

1

51

关于自动机的说明:

除I0以外,其余状态都是终态,从I0到每一状态的每条

1

路径都识别和接受一个规范句型的活前缀

如对文法句子i+i*i 进行规范归约

所得到的规范句型的活前缀都可

以由该自动机识别,如:

I0~I1 识别规范句型的活前缀E(+i*i)

I0~I6 识别规范句型的活前缀E+(i*i)

I0~I7 识别规范句型的活前缀E+T*(i)

I0~I9 识别规范句型的活前缀E+T(*i)

I0~I10 识别规范句型的活前缀E+T*F

北京航空航天大学计算机学院

1

52

2

3

状态中每个项目对该状态能识别的活前缀都是

有效的。

有效项目定义:若项目A→β1 .β2 对活前缀αβ1

有效,其条件是存在规范推导

*

E’αAw  αβ1β2 w

其中α,β1,β2 ∈ V *, w ∈ Vt *

注意:项目中圆点前的符号串成为活前缀的后缀

有效项目能预测分析的下一步动作:

E→E+T. 表示已将输入串归约为E+T,下一步应

该将E+T归约为E

*

E’ (E)(E+T)

T→T.*F 表示已将输入串归约为T,下一步动作

是移进输入符号*

注意:经移进或归约后,在栈内仍是规范句型的活前缀

北京航空航天大学计算机学院

1

53

4

DFA中的状态,既代表了分析历史又提供了展望信息

每条规范句型的活前缀都代表了一个确定的

的规范归约过程,故由状态可以代表分析历史。

由于状态中的项目都是有效项目,所以提供了

下一步可能采取的动作。

历史+展望+现实 句柄

北京航空航天大学计算机学院

1

54

2)由DFA构造SLR分析表

*

GOTO表在求LR(0)时已求出

GOTO表

文法符号

E

1

T

2

F

3

i

5

+

*

(

4

)

状态

0

1

2

3

4

5

6

7

8

9

1

(S0)

(S1)

(S2)

(S3)

(S4)

(S5)

(S6)

(S7)

(S8)

(S9)

0(S10)

6

7

8

2

9

3

5

4

3

10

5

5

4

4

6

11

7

11(S11)

北京航空航天大学计算机学院

1

55

*

求ACTION表

设k为状态编号,E为原文法识别符号,

E’为扩充文法识别符号

1

、求出文法每个非终结符的FOLLOW集合

2

、若项目A→α.aβ∈k,且a ∈Vt ,则置

ACTION[k,a] = s (移进)

北京航空航天大学计算机学院

1

56

3

、若项目A→α.∈k, 那么对输入符号a,若

a∈FOLLOW(A),则置ACTION[k,a]=rj

其中A→ α为文法G’的第j个产生式。

4

、若项目E’→E.∈k, 则置

ACTION[ k , # ] =accept

5

、ACTION表中不能用步骤2~4填入信息的

空白格,均置error

北京航空航天大学计算机学院

1

57

在状态中可有三种类型的项目,其中只有两种有移

进或归约动作:

A→α.aβ a∈Vt

A→α.

移进项目 分析动作:移进

归约项目 分析动作:归约

A→α.Bβ B∈Vn 待约项目 无动作

根据上述算法,可以构造出文法G’[E’]的ACTION

北京航空航天大学计算机学院

1

58

对文法G’[E’]

k=2 (I2)

有效项目 E→T.

k=1 (I1)

E’→E.

k=0 (I0)

E’→.E

T→T.*F

E→E.+T

E→.E+T

FOLLOW(E)={#, +, )} FOLLOW(E’)={#} E→.T

T→.T*F

T→.F

F→.(E)

F→.i

根据算法造出的ACTION表为:

北京航空航天大学计算机学院

1

59

ACTION表

输入符号a

状态s

i

+

*

(

)

#

0

1

2

3

4

5

6

S

5

S

4

S

6

accpet

r

2

S

7

r

2

r

2

7

8

9

1

0

1

1

北京航空航天大学计算机学院

1

60

两点说明:

1

. 由DFA构造出的SLR分析表,在造表时, 只需

向前看一个符号就能确定分析的动作是移进还

是归约,所以称为SLR(1)分析表,简称SLR分

析表,使用SLR分析表的分析器叫SLR分析器。

. 对文法G,若应用上述算法所造出的分析表

具有多重定义入口,分析动作不唯一, 则文法

G就不是SLR的,需要用别的方法来构造分析表。

2

北京航空航天大学计算机学院

1

61

复 习

+

自顶向下分析法 Z S

S∈L[Z]

语法分析方法:

+

自底向上分析法 SZ

(一) 自顶向下分析

概述自顶向下分析的一般过程

1

左递归问题 消除左递归的方法

存在问题

无回溯的条件

改写文法

回溯问题

超前扫描

北京航空航天大学计算机学院

1

62

两种常用方法:

2

a) 改写文法,消除左递归,回溯

b) 写递归子程序

(1) 递归子程序法

LL(1)分析器的逻辑结构及工作过程

LL(1)分析表的构造方法

1

2

3

.构造FIRST集合的算法

.构造FOLLOW集合的算法

.构造分析表的算法

(2) LL(1)分析法

LL(1)文法的定义以及充分必要条件

北京航空航天大学计算机学院

1

63

(二) 自底向上分析

归约过程:

(1)一般过程: 移进-归约过程

问题:如何寻找句柄

(2)算法:

i)算符优先分析法:

1

.分析器的构造,分析过程

根据算符优先关系矩阵来决定

是移进还是归约。

北京航空航天大学计算机学院

1

64

2

.算符优先法的进一步讨论

1

2

3

) 适用的文法类--------------引出的算符优先法的定义

) 优先关系矩阵的构造

) 什么是“句柄”,如何找

由句柄引出的最左素短语的概念。

最左素短语的定理,如何找。

ii)LR分析法

1

.概述----概念、术语 (活前缀、项目)

北京航空航天大学计算机学院

1

65

状态栈

分析表

GOTO表

1

2

)逻辑结构

)分析过程

分析动作表

控制程序

2

3

.LR分析

1

2

)构造DFA

.如何构造SLR分析表

)由DFA构造分析表

北京航空航天大学计算机学院

1

66

除了递归子程序法,其他几种方法逻辑结构很象:

输入串

符号栈(状态栈)

分析表

控制程序

(1) 对于LL(1)分析法

终结符

LL(1)分析表

符号栈

α

i

A::=αi

α

i

A

error

(自顶向下,保证最左推导)

首字符

北京航空航天大学计算机学院

1

67

栈外终结符

(2)对于算法优先分析:

符号栈

.

<

.

>

.

=

error

(3)LR分析:

状态转移GOTO表

符号栈

S0,S1…Sm

分析表

#

X1…Xm

分析动作表

北京航空航天大学计算机学院

1

68

符号

GOTO表

根据栈顶状态和栈

顶符号推导出下一

状态

态 下一

状态

终结符号

分析动作表

根据栈顶状态和输入

符号推导出下一动作

态 移进S

归约(rj)

北京航空航天大学计算机学院

1

69

将GOTO表和分析动作表压缩后得:

非终结符号

终结符号

GOTO表

S

i

i(下一状态数)

r

j

北京航空航天大学计算机学院

1

70

第五章 符号表管理技术

概述

符号表的组织与内容

非分程序结构语言的符号表组织

分程序结构语言的符号表组织

北京航空航天大学计算机学院

1

5.1 概述

(1) 什么是符号表?

在编译过程中,编译程序用来记录源程序中各种

名字的特性信息, 所以也称为名字特性表。

名 字: 程序名、过程名、函数名、用户定义类型名、

变量名、常量名、枚举值名、标号名等。

特性信息: 上述名字的种类、类型、维数、参数个数、

数值及目标地址(存储单元地址)等。

北京航空航天大学计算机学院

2

(2)建表和查表的必要性(符号表在编译过程中的作用)

源程序中变量要先声明,然后才能引用。

用户通过声明语句,声明各种名字,并给出它们的类型、

维数等信息,编译程序在处理这些声明语句时,应该将声

明中的名字及其信息登录到符号表中,同时编译程序还要

给变量分配存储单元,而存储单元地址也必须登录在符号

表中。

当编译程序编译到引用所声明的变量时(赋值或引用其值),

要进行语法语义正确性检查(类型是否符合要求)和生成相

应的目标程序,这就需要查符号表以取得相关信息。

北京航空航天大学计算机学院

3

符号表

数据区

x 简单变量 整型

a 简单变量 整型

建表,

例:int x, a, b;

分配存贮 b 简单变量 整型

L 标号

.

.

..

..

L: x := a + b;

..

1

. 语法分析和语义分析

• 说明语句、赋值语句的语法规则

.

上下文有关分析:是否声明

类型一致性检查

2

. 生成目标代码

LOAD a的地址

ADD b的地址

STO x的地址

北京航空航天大学计算机学院

4

(3)有关符号表的操作:填表和查表

填表:当分析到程序中的说明或定义语句时,应将说明或

定义的名字,以及与之有关的信息填入符号表中。

例:Procedure P( )

查表:(1) 填表前查表,检查在程序的同一作用域内名字

是否重复定义;

(2) 检查名字的种类是否与说明一致;

(3) 对于强类型语言,要检查表达式中各变量的类型

是否一致;

(4) 生成目标指令时,要取得所需要的地址。

.

........

北京航空航天大学计算机学院

5

5.2 符号表的组织与内容

(1)符号表的结构与内容

符号表的基本结构:

名字 特性(信息)

名字”域: 存放名字,一般为标识符的符号串,也可

为指向标识符字符串的指针。

北京航空航天大学计算机学院

6

名字

特性(信息)

特性”域: 可包括多个子域, 分别表示标识符的有

关信息,如:

名字(标识符)的种类:简单变量、函数、过程、

数组、标号、参数等

类型:如整型、浮点型、字符型、指针等

性质:变量形参、值形参等

值: 常量名所代表的数值

地址:变量所分配单元的首址或地址位移

大小:所占的字节数

作用域的嵌套层次:

北京航空航天大学计算机学院

7

对于数组: 维数、上下界值、计算下标变量地址所用的

信息(数组信息向量)以及数组元素类型等。

对于记录(结构、联合):域的个数,每个域的域名、

地址位移、类型等。

对于过程或函数:形参个数、所在层次、函数返回值类型、

局部变量所占空间大小等。

对于指针:所指对象类型等。

北京航空航天大学计算机学院

8

(2)符号表的组织方式

1

.统一符号表:不论什么名字都填入统一格式的符号表中

符号表表项应按信息量最大的名字设计,填表、

查表比较方便, 结构简单, 但是浪费大量空间。

2

3

.对于不同种类的名字分别建立各种符号表

节省空间, 但是填表和查表不方便。

.折中办法:大部分共同信息组成统一格式的符号表,

特殊信息另设附表,两者用指针连接。

北京航空航天大学计算机学院

9

例: begin

A : real;

B : array [1:100] of real;

:

:

end

A 简变 实型 地址

B 数组 实型

指针连接

补充

维数 上下界 首地址

北京航空航天大学计算机学院

1

0

5.3 非分程序结构语言的符号表组织

(1)非分程序结构语言: 每个可独立进行编译的程序单

元是一个不包含有子模块的单

一模块,如FORTRAN语言。

FORTRAN程序构造

主程序

主程序和子程序中可

定义common语句

子程序

或函数

北京航空航天大学计算机学院

11

(2)标识符的作用域及基本处理办法

1

. 作用域: 全局:子程序名,函数名和公共区名。

局部: 程序单元中定义的变量。

2

. 符号表的组织:

全局符号表

局部符号表

3

. 基本处理办法:

<

1> 子程序、函数名和公共区名填入全局

符号表。

北京航空航天大学计算机学院

1

2

<

<

2> 在子程序(函数)声明部分读到标识符,

造局部符号表。

有,重复声明,报错

查本程序单元局部符

号表,有无同名?

无,造表

3> 在语句部分读到标识符,查表:

有,即已声明

查本程序单元局部符

号表,有无同名?

有,全局量

无,查全局变量表

无, 无定义

标识符

北京航空航天大学计算机学院

1

3

4

5

. 程序单元结束: 释放该程序单元的局部符号表。

. 程序编译完成: 释放全部符号表。

(3)符号表的组织方式

1

. 无序符号表: 按扫描顺序建表,查表要逐项查找

查表操作的平均长度为n+1 2

北京航空航天大学计算机学院

1

4

2

3

. 有序符号表:符号表按变量名进行字典式排序

线性查表: n+1 2

Log -

折半查表:

n

1

2

. 散列符号表(Hash表):符号表地址= Hash(标识符)

解决:冲突

北京航空航天大学计算机学院

1

5

5.4 分程序结构语言的符号表组织

(1) 分程序结构语言:模块内可嵌入子模块

(2) 标识符的作用域和基本处理方法:

作用域:标识符局部于所定义的模块(最小模块)

模块中所定义的标识符作用域是定义该标识

1

符的子程序

北京航空航天大学计算机学院

1

6

1

real A;

1

real A;

2

2

A为内分程序局部变量

A为可作用于内分程序的全局变量

real A;

real A;

2

1

1

real A;

2

real A;

2

B:=A;

都是局部变量

北京航空航天大学计算机学院

1

7

过程或函数说明中定义的标识符(包括形参)其

作用域为本过程体。

2

begin

procedure P (i,j);

begin

:

2

1

L:

×

end

goto L;

P(3,5);

end ;

北京航空航天大学计算机学院

1

8

3

循环语句中定义的标识符,其作用域为该循环语句。

for … … do

begin

:

L:

×

end

Goto L;

:

北京航空航天大学计算机学院

1

9

基本处理办法:

建查符号表均要遵循标识符的作用域规定进行。

建表:不能重复,不能遗漏

查表:按标识符作用域

real x, A, B;

定义重复错误

必须填表

integer

y;

real ;

A:= + 2.0;

查表,是本层的x

:

:

B:= + 2.0

查表,是本层的x(外层的x)

北京航空航天大学计算机学院

2

0

处理方法:

a. 在程序声明部分读到标识符时(声明性出现),建表:

有,重复声明,报错

查本层符号表,有无同名?

无,填入符号表

b. 在语句中读到标识符(引用性出现),查表:

有,即已声明, 取该名字信息

是,未声明标识

符,报错

否,转到直接

外层

(局部量)

无,是否是最外层?

查本层符号表,有无同名?

(n-1)

北京航空航天大学计算机学院

2

1

c. 标准标识符的处理

主要是语言定义的一些标准过程和函数的名字,

它们是标识符的子集。

如sin con abs….

特点:1) 用户不必声明,就可全程使用

2

) 设计编译程序时,标准名字及其数目已知

处理方法:1) 单独建表:使用不便,费时。

) 预先将标准名填入名字表中

最外层

2

北京航空航天大学计算机学院

2

2

例:Pascal程序的分程序结构示例如下:

0

program main (…);

var

0

1

main

M1

P2

x, y : real; i, k: integer;

name: array [1…16] of char;

2

P33

1

:

procedure M1 (ind:integer);

var x : integer

2

q2 2

procedure P2 (j : real);

:

3

procedure P3;

var

f : array [1…5] of integer

test1: boolean;

begin

:

end; {P3}

:

北京航空航天大学计算机学院

2

3

0

program main (…);

var

x, y : real; i, k: integer;

name: array [1…16] of char;

0

1 2

begin

:

1

procedure M1 (ind:integer);

var x : integer

:

2

end;{P2}

procedure q2;

var r1,r2 : real;

begin

procedure P2 (j : real);

2

:

3

procedure P3;

var

f : array [1…5] of integer

test1: boolean;

:

begin

:

end; {P3}

P2(r1+r2);

:

end; {q2}

0

1 2

begin

:

end;{P2}

2

begin

procedure q2;

var r1,r2 : real;

:

begin

P2(x/y);

:

:

p2(r1+r2);

:

end;{M1}

end; {q2}

begin

begin

:

:

P2(x/y);

:

end;{M1}

M1(i+k);

:

begin

:

end {main}

M1(i+k);

:

北京航空航天大学计算机学院

2

4

end {main}

栈式符号表结构

主程序符号表main

M1符号表

main符号表

main符号表

M1符号表

M1符号表

P2符号表

P2符号表

P2符号表

P3符号表

栈顶

main符号表

M1符号表

q2

main符号表

M1符号表

main符号表

释放

符号表

北京航空航天大学计算机学院

2

5

name kind

type lev other inf

real

real

int

int

array

x

y

i

var

var

var

var

1

0

0

0

0

0

0

1

1

1

2

2

3

3

2

3

4

5

6

7

8

9

main

k

分程序索

引表

name var

M

ind

x

1

proc

para

var

0 1

1

2

3

7

10

12

int

int

M

1

P

j

P

2

proc

para

proc

var

real

1

0

P

P

2

1

1

3

f

array

1

1

2

3 test1

var boolean

3

北京航空航天大学计算机学院

2

6

编译q2说明部分后:

7

8

9

ind para int 1

var int 1

x

M

1

P

2

proc

para

1

real 1

1

1

0

11 q2

proc

1

1

2 r1

3 r2

var real 2

var real 2

q

2

北京航空航天大学计算机学院

2

7

编译完q2过程体:

7

8

9

ind para

int

int

x

var

P

2

proc

para

proc

10

1

real

q

2

1

当过程和函数体编译完成后,应将与之相应的

参数名和局部变量名以及后者的特性信息从符号表

中删去。

要求:给出一段程序,会画出其栈式符号表

北京航空航天大学计算机学院

2

8

第七章 源程序的中间形式

波兰表示

N-元表示

抽象机代码

北京航空航天大学计算机学院

1

7.1 波兰表示

一般编译程序都生成中间代码,然后再生成目

标代码,主要优点是可移植(与具体目标程序无关),

且易于目标代码优化。有多种中间代码形式:

波兰表示

波兰表示

算术表达式:

N-元组表示 抽象机代码

F*3.1416*R*(H+R)

转换成波兰表示: F3.1416*R*HR+*

赋值语句: A := F * 3.1416 * R * ( H + R )

波兰表示:

AF3.1416 * R * HR + * :=

北京航空航天大学计算机学院

2

#

a+b#

ab+

+

#

操作符栈

#

优先级最低

算法:

设一个操作符栈;当读到操作数时,立即输出该操作数,

当扫描到操作符时,与栈顶操作符比较优先级,若栈顶操作

符优先级高于栈外,则输出该栈顶操作符,反之,则栈外操

作符入栈。

北京航空航天大学计算机学院

3

if 语句的波兰表示

label

label

1

if 语句 :if <expr> then <stmt > else <stmt >

2

1

2

波兰表示为 :<expr><label >BZ<stmt ><label >BR<stmt >

1

1

2

2

BZ: 二目操作符

若<expr>的计算结果为0 (false),

则产生一个到<label1>的转移

BR: 一目操作符

产生一个到< label2>的转移

北京航空航天大学计算机学院

4

波兰表示为 :<expr><label >BZ<stmt ><label >BR<stmt >

1

1

2

2

由if语句的波兰表示可生成如下的目标程序框架:

<

expr>

BZ label1

stmt1>

<

BR label2

label :<stmt >

1

2

label2

其他语言结构也很容易将其翻译成波兰表示,

使用波兰表示优化不是十分方便。

北京航空航天大学计算机学院

5

7.2 N-元表示

在该表示中,每条指令由n个域组成,通常第一

个域表示操作符,其余为操作数。

常用的n元表示是: 三元式 四元式

三元式

操作符 左操作数 右操作数

表达式的三元式:w * x + ( y + z )

第三个三元

式中的操作数(1)

(2)表示第(1)和第

(2)条三元式的计

算结果。

(1) *, w, x

(2) +, y, z

(3) +, (1), (2)

北京航空航天大学计算机学院

6

条件语句的三元式:

if x > y then

z := x;

else z := y+1;

(1) -, x, y

其中:

(2) BMZ, (1), (5)

(3) :=, z, x

(4) BR, , (7)

(5) +, y, 1

(6) :=, z, (5)

(7)

BMZ:是二元操作符,测试第二

个域的值,若≤0,则按

第3个域的地址转移,

若>0,则顺序执行。

BR: 一元操作符,按第3个域

作无条件转移。

:

:

北京航空航天大学计算机学院

7

使用三元式不便于代码优化,因为优化要删除

一些三元式,或对某些三元式的位置要进行变更,由

于三元式的结果(表示为编号),可以是某个三元式的

操作数,随着三元式位置的变更也将作相应的修改,

很费事。

间接三元式:

为了便于在三元式上作优化处理,可使用间接三元式

三元式的执行次序用另一张表表示,这样在优化时,

三元式可以不变,而仅仅改变其执行顺序表。

北京航空航天大学计算机学院

8

例: A:=B+C*D/E

F:=C*D

用间接三元式表示为:

操作

三元式

1

2

3

4

5

6

. (1)

. (2)

. (3)

. (4)

. (1)

. (5)

(1) * , C, D

(2) / , (1), E

(3) + , B, (2)

(4) := , A, (3)

(5) := , F, (1)

北京航空航天大学计算机学院

9

操作符 操作数1 操作数2 结果

四元式表示

结果:通常是由编译引入的临时变量,可由编译程序

分配一个寄存器或主存单元。

例:

( A + B ) * ( C + D ) - E

+

+

, A, B, T1

, C, D, T2

式中T1,T2,T3,T4

为临时变量,由四

元式优化比较方便

*

, T1, T2, T3

一, T3, E, T4

北京航空航天大学计算机学院

1

0

7.3 抽象机代码

许多pascal编译系统生成的中间代码是一种称为

P-code的抽象代码,P-code的“P”即“Pseudo”

抽象机:

寄存器

保存程序指令的存储器

堆栈式数据及操作存储

北京航空航天大学计算机学院

11

寄存器有:

1

2

. PC-程序计数器

. NP-New指针,指向“堆”的顶部。“堆”用来存放由New

生成的动态数据。

3

. SP- 运行栈指针,存放所有可按源程序的数据声明直接

寻址的数据。

4

5

. BP-基地址指针,即指向当前活动记录的起始位置指针。

. 其他,(如MP-栈标志指针,EP-极限栈指针等)

北京航空航天大学计算机学院

1

2

计算机的存储大致情况如下:

栈底

运行栈

P-code指令

BP

SP

当前模块活

动记录

(数据段)

NP

PC

堆底

常量区

程序指令存储器

北京航空航天大学计算机学院

1

3

运行P-code的抽象机没有专门的运算器或

累加器,所有的运算(操作)都在运行栈的栈顶进

行,如要进行d:=(a+b)*c的运算,生成P-code序

列为:

取a LOD a

取b LOD b

栈底:

运行栈

a单元

b单元

c单元

d单元

a

+

ADD

取c

*

送d

LOD c

MUL

STO d

SP

P-code实际上是波兰表示形式的中

间代码

b

栈顶

北京航空航天大学计算机学院

1

4

第八章 错误处理

概述

错误分类

错误的诊察和报告

错误处理技术

北京航空航天大学计算机学院

1

8.1 概述

1

. 必备功能之一

正确的源程序:通过编译生成目标代码

错误的源程序:通过编译发现并指出错误

. 错误处理能力

2

(1) 诊察错误的能力

(2) 报错及时准确

(3) 一次编译找出错误的多少

(4) 错误的改正能力

(5) 遏止重复的错误信息的能力

北京航空航天大学计算机学院

2

8.2 错误分类

从编译角度,将错误分为两类:语法错误和语义错误

语法错误:源程序在语法上不合乎文法

如:

A[ I,J := B +* C

语义错误主要包括:程序不符合语义规则或

超越具体计算机系统的限制

北京航空航天大学计算机学院

3

语义规则:

标识符先说明后引用

标识符引用要符合作用域规定

过程调用时实参与形参要一致

参与运算的操作数类型一致

下标变量下标不能越界

超越系统限制:

数据溢出错误

符号表、静态存储分配数据区溢出

动态存储分配数据区溢出

北京航空航天大学计算机学院

4

8.3 错误的诊察和报告

错误诊察:

1

. 违反语法和语义规则以及超过编译系统限制的错误。

编译程序: 语法和语义分析时

语义分析要借助符号表)

2

. 下标越界,计算结果溢出以及动态存储数据区溢出。

目标程序: 目标程序运行时

对此,编译程序要生成相应的目标程序作检查

和进行处理

北京航空航天大学计算机学院

5

错误报告:

. 出错位置:即源程序中出现错误的位置

实现:行号计数器 line_no

单词序号计数器 char_no

一旦诊察出错误,当时的计数器内容就是出错位置

1

2

. 出错性质:

可直接显示文字信息

可给出错误编码

北京航空航天大学计算机学院

6

3

. 报告错误的两种方式:

(1) 分析完以后再报告(显示或者打印)

编译程序可设一个保存错误信息的数据区(可

用记录型数组),将语法语义分析所诊察到的错误

送数据区保存,待源程序分析完以后,显示或打印

错误信息。

例:A[x , y :=B+*C

源程序行号 错误序号 错误性质

x x

x x

6

缺少“]”

10 表达式语法错误

北京航空航天大学计算机学院

7

(2) 边分析边报告

可以在分析一行源程序时若发现有错,立即输

出该行源程序,并在其下输出错误信息。

Line-no A[x , y

:= B+ *C

缺“]”orn 表达式语法错 m

有时候报错不一定十分准确

位置和性质),需进一步分析

错误编号

begin

.......

i := 1 step 1 until n do

...........

.

.

end

北京航空航天大学计算机学院

8

8.4 错误处理技术

发现错误后,在报告错误的同时还要对错误进行

处理,以方便编译能进行下去。目前有两种处理办法:

1

. 错误改正:指编译诊察出错误以后,根据文法进

行错误改正。

如:A[i , j :=B+*C

要正确地改正错误

是很困难的

但不是总能做到,如A:=B-C*D+E)

2

. 错误局部化处理:指当编译程序发现错误后,尽可

能把错误的影响限制在一个局部

的范围,避免错误扩散和影响

程序其他部分的分析。

北京航空航天大学计算机学院

9

(1) 一般原则

当诊察到错误以后,就暂停对后面符号的

分析, 跳过错误所在的语法成分然后继续往下分析。

词法分析:发现不合法字符,显示错误,并跳

过该标识符(单词)继续往下分析。

语法语义分析:跳过所在的语法成分(短语或语

句),一般是跳到语句右界符,

然后从新语句继续往下分析。

北京航空航天大学计算机学院

1

0

(2) 错误局部化处理的实现(递归下降分析法)

cx: 全局变量,存放错误信息。

•用递归下降分析时,如果发现错误,便将有

关错误信息(字符串或者编号)送CX,然后转出

错误处理程序;

•出错程序先打印或显示出错位置以及出错信息,

然后跳出一段源程序, 直到跳到语句的右界符

(如:end)或正在分析的语法成分的合法后继符号

为止, 然后再往下分析。

北京航空航天大学计算机学院

11

if<B> then <stmt>[else< stmt >];

例:条件语句分析:

有如下分析程序:

procedure if_stmt;

begin

nextsym;

B;

/*读下个单词符号*/

/*调用布尔表达式处理程序*/

if not class=‘then’ then

begin

cx :=‘缺then’

error;

/*错误性质送cx*/

/*调用出错处理程序*/

end;

else

begin

nextsym;

statement

end;

if class=‘else’then

begin

nextsym;

statement;

end

北京航空航天大学计end if_stmt;

2

局部化处理的出错程序为:

procedure error;

begin

write(源程序行号, 序号, cx)

repeat

nextsym;

until class = ‘;’ or class = ‘end’ or class = ‘else’

end error;

跳过太多

real x, 3a, a, bcd, 2fg;

北京航空航天大学计算机学院

1

3

(3) 提高错误局部化程度的方法

设S1: 合法后继符号集(某语法成分的后继符号)

S2: 停止符号集

(跳读必须停止的符号集)

进入某语法成分的分析程序时:

S1:= 合法后继符号

S2:= 停止符号

北京航空航天大学计算机学院

1

4

当发现错误时: error(S1,S2)

procedure error(S1,S2)

begin

write(line_no, char_no, cx);

repeat

nextsym

until(class in S1 or class in S2 );

end

if<B> then <stmt>[else< stmt >]

若<B>有错,则可跳到then,

若<stmt>有错,则可跳到else。

北京航空航天大学计算机学院

1

5

3

.目标程序运行时错误检测与处理.

下标变量下标值越界

计算结果溢出

动态存储分配数据区溢出

·

在编译时生成检测该类错误的代码。

对于这类错误,要正确的报告出错误位置很难,因为

目标程序与源程序之间难以建立位置上的对应关系

一般处理办法:

当目标程序运行检测到这类错误时,就调用异常

处理程序,打印错误信息和运行现场(寄存器和存储

器中的值)等, 然后停止程序运行。

北京航空航天大学计算机学院

1

6

第九章 语法制导翻译技术

翻译文法(TG)和语法制导翻译

属性翻译文法(ATG)

自顶向下语法制导翻译

翻译文法的自顶向下语法制导翻译

属性文法的自顶向下语法制导翻译

自底向上的语法制导翻译(自学)

北京航空航天大学计算机学院

1

9

.0 本章导言

词法分析,语法分析:解决单词和语言成分的识别

及词法和语法结构的检查。语法结构可形式化地用

一组产生式来描述。给定一组产生式,能够很容易

地将其分析器构造出来。

本章要介绍的是语义分析和代码生成技术。

程序语言的语义形式化描述目前有三种基本描述方

法,即:

操作语义

指称语义

公理语义

北京航空航天大学计算机学院

2

9.1 翻译文法和语法制导翻译

有上下无关文法G[E]:

1

2

3

. E →E+T

. E →T

. T →T*F

4. T →F

5. F →(E)

6. F →i

此文法是一个中缀算术表达式文法

翻译的任务是: 中缀表达式 逆波兰表示

a+b*c abc*+

假如翻译任务是要将中缀表达式简单变换为波兰后

缀表示,只需在上述文法中插入相应的动作符号。

北京航空航天大学计算机学院

3

1

2

3

. E →E+T@+

. E →T

. T →T*F @*

4. T →F

5. F →(E)

6. F →i@i

其中:

@+,@*,@i 为动作符号。@为动作符号标记,后面为字符串。

在本例中,其对应语义子程序的功能是要输出打印动作

符号标记后面的字符串。

所以,产生式1:E→E+T@+ 的语义是分析E, + 和T,输出+

产生式6:F→i @ i的语义是分析i,输出i

北京航空航天大学计算机学院

4

下面给出输入文法和翻译文法的概念:

输入文法:未插入动作符号时的文法。

由输入文法可以通过推导产生输入序列。

翻译文法:插入动作符号的文法。

由翻译文法可以通过推导产生活动序列。

输入序列

动作序列

北京航空航天大学计算机学院

5

例:( i+i ) * i

可以用输入文法推导:

E  T  T*F  F*F (E)*F (E+T)*F

* (i+i)*i

用相应的翻译文法推导,可得:

E  T

T*F@*

F*F@*

(E)*F@*

(E+T@+)*F@* * (i@i+i@i @+)*i@i@*

北京航空航天大学计算机学院

6

活动序列:由翻译文法推导出的符号串,由终结符和动作

符号组成。

从活动序列中,抽去动作符号,则得输入序列(i+i)*i

从活动序列中,抽去输入序列,则得动作序列,执行动

作序列,则完成翻译任务:

@

i@i@+@i@*  ii+i*

定义9.1

翻译文法是上下文无关文法,其终结符号集由输入符

号和动作符号组成。由翻译文法所产生的终结符号串称为

活动序列。

北京航空航天大学计算机学院

7

上例题中的翻译文法为:

G = (V , V , P, E)

T

n

t

Vn = {E, T, F}

Vt = {i, +, *, (, ), @ +, @ *, @i}

P = {E→E+T@ +, E→T, T→T*F@*, T→F, F→(E),

F→i@i}

符号串翻译文法:若插入文法中的动作符号对应的语义子

程序是输出动作符号标记@后的字符串的文法。

语法导制翻译:按翻译文法进行的翻译。

给定一输入符号串,根据翻译文法获得翻译该符号串的动作

序列,并执行该序列所规定的动作的过程。

北京航空航天大学计算机学院

8

语法导制翻译的实现方法:

在文法的适当位置插入语义动作符号,当按文法分析到动作

符号时就调用相应的语义子程序,完成翻译任务。

翻译文法所定义的翻译是由输入序列和动作序列组成的对偶集。

如:(i+i)*i, @i@i@+@i@* →ii+i*

i+i*i @i@i@i@*@+

因此,给定一个翻译文法,就给定了一个对偶集。

北京航空航天大学计算机学院

9

9.2 属性翻译文法

在翻译文法的基础上,可以进一步定义属性文法,

翻译文法中的符号,包括终结符、非终结符和动作符号

均可带有属性,这样能更好的描述和实现编译过程。

属性可以分为两种:

综合属性

继承属性

北京航空航天大学计算机学院

1

0

9

.2.1 综合属性

基本操作数带有属性的表达式文法G[E]

1

2

3

.E→E+F

.E→T

.T→T*F

4.T→F

5.F→(E)

6.F→i

 C

其中↑C是综合属性符号,↑为综合属性标记,

c为属性变量或者属性值。

此文法能够产生如下的输入序列:

i

 3 +i  9) * i  2

北京航空航天大学计算机学院

11

根据给定的文法,可写出该输入序列的语法树

E

T

E

T

24

24

T

F

F

i

T

F

*

*

12

 2

自底向上

F

E

12

 2

i

 2

(

)

的属性计算

(

)

E

12

E + T

E + T

3

3

 9

 9

T F 所以,用表示属性 T

计算是自底向上的,

F

F

F

 3

i

 9

i

称为综合属性。

 9

i

 3

i 3

北京航空航天大学计算机学院

1

2

为了形式地表示上述表达式的属性求值过程,可以改写上述文法:

产生式 求值规则

1

2

3

4

5

6

.E

.E

.T

.T

.F

.F

 p4

 p3

 p2

 p2

 p1

 p1

E

T

 q5

 q4

+ T

 r2

p

p

4:=q 5+r

3:=q

2:=q 3*r

2

;

4

;

T

 q3

* F

 r1

p

1

;

F

 q2

p

p

p

2:=q

2

1

1

;

;

;

(E  q1)

1

:=q

:=q

i

 q1

1

说明:

求值规则:综合属性是自右向左,

自底向上求值。

p,q,r为属性变量名。

属性变量名局部于每个产生

式,也可使用不同的名字。

北京航空航天大学计算机学院

1

3

9

.2.2 继承属性

考虑下列文法:G[<说明>]:

1. <说明> → Type id <变量表>

2. <变量表> →,id <变量表>

3

. <变量表> →ε

其中

Type: 类型名(值:int, real, bool等)

id: 变量名(值:指向该变量符号表项的指针)

符号表

上述文法所产生的语句:int A,BC

A 整型

该文法的翻译任务:将声明的变量填入符号表

完成该工作的动作符号:@set_table

BC 整型

北京航空航天大学计算机学院

1

4

翻译文法:

1

. <说明> →Type id @set_table <变量表>

. <变量表> →,id @set_table <变量表>

2

3

. <变量表> →ε

填表时需要的信息:类型,名字,以及填的位置(可以用全程

变量或指针)

如何得到?

类型和名字在词法分析时得到,可设两个综合属性。

北京航空航天大学计算机学院

1

5

Type  t

id  n

t中放类型值

n中放变量名

填表动作符号也可带有属性:

@set_table↓t1 , n1

↓t ,n 可从前面得到,所以称为继承属性,

1

1

继承前面的值

↓t2 同上

<变量表> ↓t

2

属性翻译文法:

1

2

3

.<说明> →Type id @set_table

<变量表>

t ,t := t; n := n;

2 1

t

n

↓t1,n1

↓t2

1

.<变量表> →,id @set_table

<变量表>↓t3 t ,t := t ; n := n;

3 1

t2

 n

↓t1,n1

2

1

.<变量表> →ε

t2

北京航空航天大学计算机学院

1

6

例:int A,BC  Type

id A , idBC

int

语法树:

<说明>

Type int

id A

@ set_table↓int,A

<变量表> ↓int

,

idBC @ set_table ↓int BC <变量表> ↓int

ε

继承属性求值:自左向右,自顶向下

综合属性求值:自右向左,自底向上

北京航空航天大学计算机学院

1

7

int A, BC 的分析翻译过程:

<

说明>  Type id @ set_table <变量表>

↓t

t

n1

↓t,n1

+

Type id @ set_table

t

n1

↓t,n1

id @ set_table

n2 ↓t,n2

符号表

A int

BC int

p

北京航空航天大学计算机学院

1

8

9

.2.3 ( 1 ) L-属性翻译文法(L-ATG)

这是属性翻译文法中较简单的一种。其输入文法要求是LL(1)

文法,可用自顶向下分析构造分析器。在分析过程中可进行属性求

值。

定义9.2:

L-属性翻译文法是带有下列说明的翻译文法:

1. 文法中的终结符,非终结符及动作符号都带有属性,且每个属性都有一个

值域。

2. 非终结符及动作符号的属性可分为继承属性和综合属性。

3. 开始符号的继承属性具有指定的初始值。

4. 输入符号(终结符号)的每个综合属性具有指定的初始值。

5. 属性的求值规则:

北京航空航天大学计算机学院

1

9

属性的求值规则:

继承属性:

1) 产生式左部非终结符号的继承属性值,取前面产生式

右部该符号已有的继承属性值。

2)产生式右部符号的继承属性值,用该产生式左部符号

的继承属性或出现在该符号左部的符号的属性值进行

计算。

北京航空航天大学计算机学院

2

0

综合属性:

1)产生式右部非终结符号的综合属性值,取其下部产

生式左部同名非终结符号的综合属性值。

2)产生式左部非终结符号的综合属性值,用该产生式

左部符号的继承属性或某个右部符号的属性进行计

算。

3)动作符号的综合属性用该符号的继承属性或某个右

部符号的属性进行计算。

适合在自顶向下分析过程中求值

北京航空航天大学计算机学院

2

1

例:A→BC

求值顺序:

1

) A的继承属性 (若A为开始符号,则有指定值,否则由上面

产生式右部符号的继承属性求得)

2

) B的继承属性 (由A的继承属性求得)

3

) B的综合属性 (由下面产生式中左部符号为B的综合属性

求得)

4

) C的继承属性 (由A的继承属性和B的属性求得)

5

) C的综合属性 (由下面产生式中左部符号为C的综合属性

求得)

6

) A的综合属性 (由前述(2),即A的继承属性或产生式某

右部符号属性计算)

北京航空航天大学计算机学院

2

2

(2) 简单赋值形式的L_属性翻译文法(SL-ATG)

一般属性值计算: x:=f ( y , z )

SL-ATG属性值计算: x:=某符号的属性值或常量。

例 x:=y, x,y,z:=17 —— 称为复写规则

为了实现上的方便,常希望文法符号的属性求值规则为上述简

单形式的。为此,对现有的L-ATG的定义做一点改变,

从而形成一个称为简单赋值形式的L-ATG。

北京航空航天大学计算机学院

2

3

•定义9.4 一个L-ATG被定义为简单赋值形式的(SL-ATG),

当且仅当满足如下条件:

1

. 产生式右部符号的继承属性是一个常量,它等于左部符号

的继承属性值或等于出现在所给符号左边符号的一个综合

属性值。

2

.产生式左部非终结符号的综合属性是一个常量,它等于左

部符号的继承属性值或等于右部符号的综合属性值。

因此,一个简单赋值形式的L-ATG除动作符号外,其余符号的

属性求值规则其右部是属性或是常量。

北京航空航天大学计算机学院

2

4

L-ATG  SL-ATG

给定一个L-ATG,如何找一个等价的赋值形式的L-ATG?

考虑产生式:

<

A > → a < B > < C > ,

I : = f ( R , S )

I

R

S

显然: 该属性求值规则不是简单赋值形式的,因为它需要

对f求值。

北京航空航天大学计算机学院

2

5

第一步:设动作符号“@ f ” 表示函数f求值,该动作符

号有两个继承属性和一个综合属性。

@f

且 S := f ( I , I )

1 2

I ,I  S

1

1

2

1

第二步:修改产生式

1

2

3

. 插入“@ f ” (在适当位置)

. 引进新的复写规则(将R , S 赋给I 和I , f值赋给S )

1

2

1

. 删去原有包含f 的规则

北京航空航天大学计算机学院

2

6

<

A >→a < B > @ f

< C > ↓I,

↓I , I  S

R

 S

1

2

1

I := R , I := S, S := f(I I ), I := S

1

2

1

1, 2

1 .

该文法是简单赋值形式的L-ATG.

注意: 无参函数过程作为常数处理,如

<

A > → < B > < C >

x ,y := NEWT

x

 y

北京航空航天大学计算机学院

2

7

9.3 自顶向下语法制导翻译

9

9

.3.1 翻译文法的自顶向下翻译

—递归下降翻译器

.3.2 属性翻译文法的自顶向下翻译的实现

—递归下降属性翻译器

北京航空航天大学计算机学院

2

8

9

.3.1 翻译文法的自顶向下翻译——递归下降翻译器

按翻译要求,在文法中插入语义动作符号,在分析过程

中调用相应的语义处理程序,完成翻译任务。

例:输入文法

翻译文法(符号串翻译文法)

1

2

3

. < S >

. < S >

. < A >

a< A >< S >

b

< S >

< S >

< A >

a < A > @ x < S >

b @ z

c< A >< S >b

c @ y < A > < S > @ v b

@ w

<

A >

4

. < A > ε

北京航空航天大学计算机学院

2

9

主程序

if CLASS = a or b

PROCS;

主程序

if CLASS = a or b

then

then

PROCS;

if CLASS ≠ #

ERROR;

if CLASS ≠ 右界符 then

ERROR;

then

ACCEPT;

ACCEPT;

过程 PROCS

case CLASS of

a : P1;

过程 PROCS

case CLASS of

a : P1 ;

b : P2;

b : P2 ;

其它:ERROR;

其它:ERROR;

end of

case;

end of

case.

北京航空航天大学计算机学院

3

0

P :

/ *产生式1的代码* /

NEXTSYM;

P1:

/ *产生式1的代码* /

NEXTSYM;

1

PROCA;

PROCS;

RETURN;

PROCA;

OUT( x ) ;

PROCS;

P :

/ *产生式2的代码* /

RETURN;

2

NEXTSYM;

RETURN;

P :

/ *产生式2的代码* /

2

NEXTSYM;

OUT( z ) ;

RETURN;

过程 PROCA

过程 PROCA

北京航空航天大学计算机学院

3

1

9

.3.2 属性文法自顶向下翻译的实现——递归下降翻译器

方法:

• 对于每个非终结符号都编写一个翻译子程序(过程)。根据该

非终结符号具有的属性数目,设置相应的参数。

U↓x, y→ …

继承属性:声明为赋值形参

综合属性:声明为变量形参

Procedure U(x,y);

x—赋值形参

y—变量形参

北京航空航天大学计算机学院

3

2

过程调用语句的实参:

继承属性 : 继承属性值

综合属性 : 属性变量名(传地址,返回时有值)

关于属性名的约定:

1

)产生式左部的同名非终结符使用相同的属性名。

<

L> →e <R>

<L> →e <R>

↑x↓y ↓I ↓J

递归下降分析法所必须)

↑a↓b ↓I

↓J

<L> →<H>

<L> →<H>

x↓y

)具有相同值的属性取相同的属性名。

↓z↑w

↑x↓y

↓z↑w

2

具有简单赋值形式的属性变量名取相同的属性名,可删

去属性求值规则。

<

S>→I <B> <C> b,c:= a

a

↓b

↓c

<

S>→I <B> <C>

x

↓x

↓x

北京航空航天大学计算机学院

3

3

下面通过一个例子,较详细地介绍如何构造属性文法 的递归下降翻

译器。

例:有如下属性翻译文法G[< S>]

R2:= R1

T2:= T1

Q2:= Q1

1.

< S >R1 → a T < A > Q @ x

T , R < S >

Q

2

1

1

2

2

2

.

.

< S >R1 →b @ zR2 ,

R2 : = R1

3

< A > P → CU @ y < A > Q < S > z @ v P b

U

1

2

U := U , P := Q + U , z := U -3

2

1

1

1

4.

< A > P →@ w P := 8

对简单赋值形式的属性变量取相同的属性名,其求值规则可

以删去。开始符号的继承属性R1=7。

北京航空航天大学计算机学院

3

4

1.

< S >R → a T < A > Q @ x T , R < S >

Q

2

.

.

< S >R →b @ zR ,

3

< A > P→ Cu @ yu < A > Q < S > z @ v P b

P := Q + U, z := U-3

4.

< A > P →@ w P := 8

北京航空航天大学计算机学院

3

5

全局变量和过程声明:

CLASS;

TOKEN;

/ * 存放单词类别码* /

/ * 存放单词值*/

NEXTSYM;

/ *词法分析程序,每调用一次单词类别码 CLASS,

单词值 TOKEN,该符号指针指向下一个单词*/

主程序:

NEXTSYM;

PROCS(7);

if CLASS ≠ 右界符 then

ERROR;

ACCEPT

过程 PROCS(R)

R;

/* 值形参声明*/

北京航空航天大学计算机学院

3

6

case

a:

CLASS

P1 ;

of

b:

P2 ;

其它: ERROR;

end of case;

P1 :

T , Q ;

/ * 产生式1的代码* /

/ * 局部变量声明* /

T := TOKEN;

NEXTSYM;

PROCA(Q)

/ * 单词值赋给终结符的综合属性* /

OUT(X ↓T,R);

PROCS(Q)

RETURN;

北京航空航天大学计算机学院

3

7

P :

/ * 产生式2的代码* /

NEXTSYM;

2

OUT(Z ↓R );

RETURN;

过程 PROCA(P)

P;

/*变量形参声明*/

case CLASS of

C : p3

其它: p4

end of

case;

北京航空航天大学计算机学院

3

8

P3:

U , Q , Z ;

P4:

P := 8;

/*局部变量声明*/

U := TOKEN;

NEXTSYM;

Z := U – 3 ;

OUT(yU);

OUT(w);

RETURN;

插在U已知,使用Z之前

PROCA(Q); 返回时有值

插在Q,U已知,使用P之前。

P := Q+U;

PROCS(Z);

OUT(VP );

if CLASS ≠ b then ERROR;

NEXTSYM;

RETURN;

北京航空航天大学计算机学院

3

9

一个例子

例:构造将算术表达式翻译成四元式的属性翻译文法,并写出递

归下降分析程序。由该属性翻译文法来描述翻译过程。

翻译的输入: 算术表达式

翻译的输出: 四元式

a + b

ADD , P , P , P

r

a

b

其中P , P , P 为变量a , b和结果单元的地址。

a

b

r

表达式:

输入:

(a + b) * c

( Id  + Id  ) * Id 

4

1

2

Id由词法分析程序返回,

.....综合属性,变量在数据区地址。

1

输出:

ADD, 1, 2, 3

MULT, 3, 4, 5

北京航空航天大学计算机学院

4

0

数据区:

a

1

2

b

3

4

部分结果

c

部分结果

5

北京航空航天大学计算机学院

4

1

(⑴)翻译文法设计:

E →E+T@ADD

T →F

E →T

F →(E)

F →Id

T →T*F @MULT

@

ADD为输出ADD四元式的动作符号

MULT为输出MULT四元式的动作符号

对应于完成翻译

的语义动作程序

@

在文法中的插入位置:在分别处理完成两个操作数之后

输入序列:(a+b)*c

翻译文法产生的活动序列: (a+b@ADD)*c@MULT

动作符号序列:@ADD @MULT 反映生成四元式的顺序,语法分析过

程中语义程序的调用顺序。

北京航空航天大学计算机学院

4

2

(2) 属性翻译文法的设计

输入符号(操作数)有一个综合属性,它是该符号在数据区

的地址。

每个非终结符有一个综合属性,该属性是由它产生的代表该

子表达式在数据区的地址。

动作符号有三个继承属性,它们分别是左右操作数和运算结

果在数据区地址。

这样可得表达式的属性翻译文法

—可将中缀表达式翻译成四元式

北京航空航天大学计算机学院

4

3

x, p := NEW y := q z := r

1

2

3

4

5

6

. E  x →E  q +T  r @ADDy , z , p

x := p

. E

 x  p

→ T

x, p := NEW y := q z := r

. T  x →T  q *F  r @MULTy , z , p

x := p

x := p

x := p

. T

. F

. F

 x

 x

 x

→ F

→(E

→Id

 p

 p

)

 p

说明:

Id的综合属性p是数据区地址,NEWT为系统过程,

返回数据区地址。

北京航空航天大学计算机学院

4

4

反映属性求值的语法树:

E

T

 5

 5

*

T

F

 3

 3

 3

F

 4

Id

 4

@MULT

↓3 , 4 , 5

( E

 1

)

E

T

F

+ T

F

 2

 2

 1

 1

Id

 2

@ADD

↓1 , 2 , 3

Id

 1

北京航空航天大学计算机学院

4

5

语义动作程序如何设计在后面介绍

@

ADD ↓y , z , p  fprintf(objfile, “ADD %d %d %d \n”, y, z, p)

(⑶)写递归下降翻译程序

(留作作业)

北京航空航天大学计算机学院

4

6

小结:

本章介绍了语法制导翻译的概念和技术,是在抽象层次上讲的。

第十章对过程语言的编译就是采用该法。

翻译文法:在输入文法中插入语义动作符号(完成翻译任务的语义

子程序)

属性翻译文法:文法符号(包括动作符号)可带有属性,并定义相

应的属性求值规则,就成为属性翻译文法。比翻译

文法能更细地描述翻译过程。(属性有综合属性和

继承属性之分)

北京航空航天大学计算机学院

4

7

自顶向下的语法制导翻译(递归下降翻译)

在本章的基础上,第十章将介绍典型的过程语言的语法制

导翻译。

本章未讲的部分不要求

北京航空航天大学计算机学院

4

8

第十章 语义分析和代码生成

语义分析的概念

栈式抽象机及其汇编指令

声明的处理

表达式的处理

赋值语句的处理

控制语句的处理

过程调用和返回

北京航空航天大学计算机学院

1

假定:

源语言: 通用的过程语言

生成代码:栈式抽象机的(伪)汇编程序

翻译方法:自顶向下的属性翻译

语法成分翻译子程序参数设置:

继承属性为值形参

综合属性为变量形参

语法成分翻译动作子程序参数设置:

继承属性为值形参

综合属性不设形参,而作为动作子程序的返回值

由RETURN语句返回)

北京航空航天大学计算机学院

2

1

0.1 语义分析的概念

1

2

3

、上下文有关分析:即标识符的作用域

、类型的一致性检查

、语义处理:

声明语句:其语义是声明变量的类型等,并不要求

做其他的操作。

编译程序的工作是填符号表,登录名字

的特征信息,分配存储。

执行语句:语义是要做某种操作。

语义处理的任务:按某种操作的目标结构

生成代码。

北京航空航天大学计算机学院

3

用上下文无关文法只能描述语言的语法结构,

而不能描述其语义。

例如,对于有嵌套子程序结构的程序段:

BEGIN … BEGIN αINT I βI END … I … END

若存在文法规则:VAR ::= I

第一次I的归约正确

第二次I的归约错误

BEGIN … <BLOCK> … I ... END

BEGIN … δVAR ... END

δ∈V*且不包含变量I的声明

文法规则应改为:INT I βVAR ::= INT I βI

北京航空航天大学计算机学院

4

然而上下文有关文法不仅构造困难,

而且其分析器十分复杂,分析效率又低,

显然是不实用的

因此,通常我们把与语义相关的上下文有关

信息填入符号表中,并通过查符号表中的这些信

息来分析程序的语义是否正确

北京航空航天大学计算机学院

5

1

0.2 栈式抽象机及其汇编指令

栈式抽象机:由三个存储器、一个指令寄存器

和多个地址寄存器组成。

存储器: 数据存储器 (存放AR的运行栈)

操作存储器 (操作数栈)

指令存储器

北京航空航天大学计算机学院

6

计算机的存储大致情况如下:

栈底

运行栈

P-code指令

BP

SP

当前模块活动

记录(数据段)

NP

PC

(堆底)

程序指令存储器

北京航空航天大学计算机学院

7

栈式抽象机指令代码如下:

指令名称

加载指令

操作码

地址

指令意义

LOD

D

将D 的内容→栈顶

立即加载

地址加载

存储

LDC

LDA

STO

ST

常量

(D)

D

常量→栈顶

变量D 的地址→栈顶

存入

栈顶内容 →变量D

间接存

间接存

@D

将栈顶内容→D 所指单元

将栈顶内容→次栈顶所指单元

栈顶和次栈顶内容相加,结果留栈顶

次栈顶内容减栈顶内容

STN

ADD

SUB

MUL

……

北京航空航天大学计算机学院

8

指令名称

等于比较

操作码

地址

指令意义

EQL

不等比较

大于比较

小于比较

大于等于

小于等于

NEQ

GRT

LES

GTE

LSE

次栈顶内容与栈顶内容比较,

结果(1 或0)留栈顶

逻辑与

逻辑或

逻辑非

转子

AND

ORL

NOT

JSR

lab

M

分配

ALC

在运行栈顶分配大小为M 的活动记录区

北京航空航天大学计算机学院

9

1

0.3 声明的处理

语义的表示:

给出语言结构的属性翻译文法来说明其语义及语义动作,

并把这些语义动作插入属性翻译文法产生式中的适当位置。

编译程序的任务:

编译程序处理声明语句要完成的主要任务为:

1

2

) 分离出每一个被声明的实体,并把它们的名字填入符号表中

) 把被声明实体的有关特性信息尽可能多地填入符号表中

对于已声明的实体,在处理对该实体的引用时要做的事情:

1

2

) 检查对所声明的实体引用(种类,类型等)是否正确

) 根据实体的特征信息,例如类型,所分配的目标代码地址(可能

为数据区单元地址,或目标程序入口地址)生成相应的目标代码

北京航空航天大学计算机学院

1

0

声明有常量声明,变量(包括简单变量,数组变量

和记录变量等)和过程(函数)声明等,这里主要讨论

常量声明和简单变量、数组声明的处理。

声明的两种方式:

(1) 类型说明符放在变量的前面。如:C语言: int a;

在填表时已知类型和a的值(名字):直接填入符号表。

(2) 类型说明符放在变量的后面,如:Pascal, PL/1,Ada

等,需要返填。

如PL/I声明语句:

DECLARE( X, Y(N), YTOTAL) FLOAT;

北京航空航天大学计算机学院

11

声明语句的输入文法为:

<

<

<

declaration>→DECLARE ‘ (’<entity list>‘)’<type>

entity list>→<entity name> | <entity name> , <entity list>

type>→FIXED | FLOAT | CHAR

属性翻译文法为:

declaration>→DECLARE @dec_on ‘ (’<entity list> ‘)’

<

x

<

type> @fix_up

t

↓x, t

<

<

entity list>→<entity name> @name_defn

n

↓n

|

<entity name> , @name_defn <entity list>

n

↓n

type> →FIXED | FLOAT | CHAR

t

↑t

↑t

↑t

北京航空航天大学计算机学院

1

2

动作程序

dec_on 是把符号表当前可用表项的入口地址(指向符

@

x

号表入口的指针,或称 表项下标值)赋给属性变量x 。

@

name_defn 是将由各实体名所得的n继承属性值,依次填

n

入从x开始的符号表中。

注:显然应有内部计数器或内部指针,指向下一个该填的符号表项。

@

fix_up 是将类型信息t 和相应的数据存储区分配地址填入

x, t

从x 位置开始的符号表中。(反填)

当然,如果声明语句中,类型说明符放在头上,就无需“反填”处理了。

北京航空航天大学计算机学院

1

3

1

0.3.1 常量类型声明处理

常量标识符通常被看作是全局名。

常量声明的ATG如下:

const del> →constant <type> <entity> :=<const expr>

↑c, s

<

t

↑n

@insert

t,n,c,s

<

<

type> →real |integer |string

↑t ↑t ↑t

t

const expr> →<integer const > |<real const>

c, s

↑c, s

↑c, s

|

<string const>↑c, s

由该文法产生的一个声明实例为:

constant integer SYMBSIZE := 1024;

北京航空航天大学计算机学院

1

4

翻译处理过程为:

const del> →constant <type> <entity> :=

<

t

↑n

<

const expr> @insert

c, s

↓t,n,c,s

先识别类型(integer),将它赋给属性t;然后识别常量名字

SYMBSIZE), 将它赋给属性n;最后识别常量表达式,并将

其值赋给c,其类型赋给属性s 。

@insert 的功能是:

检查声明的类型t 和常量表达式的类型s 是否一致,若

不一致,则输出错误信息

把名字n,类型t 和常量表达式的值c 填入符号表中

北京航空航天大学计算机学院

1

5

1

0.3.2 简单变量声明处理

ATG文法:

svar del> →<type> <entity> @svardef

<

t , i

↑n

↓t, i, n

@

allocsv

i

<

type> →real |integer |character (<number>)

t, i ↑t, i ↑t, i ↑t ↑i

|

logical

t, i

n: 变量名

t: 类型值

简单变量声明的例子:

real x ;

i: 该类型变量所需

数据空间的大小

integer j;

character ( 20 ) s ;

北京航空航天大学计算机学院

1

6

@svardef动作符号是把n,i 和t 填入符号表中。

procedure svardef( t, i, n );

j := tableinsert ( n, t, i ); /*将有关信息填入符号表*/

if j = 0

then errmsg ( duplident , statementno);

else if j = -1 //符号表已满

then errmsg( tblovflow, statementno);

end svardef;

//填表时要检查是否重名

procedure allocsv( i );

codeptr := codeptr + i ; //codeptr 为分配地址指针

end allocsv;

@allocsv 和@svardef 可以合并

北京航空航天大学计算机学院

1

7

对于变长字符串(或其它大小可变的数据实体),往往需要

采用动态申请存储空间的办法把可变长实体存储在堆中。我们可

通过指向存放该实体数据区的指针来引用该实体,有时还应得到

该实体存储空间的大小信息,并一起填入符号表内。

1

0.3.3 数组变量声明的处理

对于静态数组,即数组的大小在编译时是已知的,编译程序

在处理数组声明时,可建立一个数组模板(又称为数组信息向量)

以便以后的程序中引用该数组元素时,可按照该模板提供的信

息,计算数组元素(下标变量)的存储地址。

对于动态数组,其大小只有在运行时才能最后确定。我们

在编译时仅为该模板分配一个空间,而模板本身的内容将在运

行时才能填入。

北京航空航天大学计算机学院

1

8

大部分程序设计语言,数组元素是按行(优先)存放在存储器

中的*,如声明数组 array B ( N, -2: 1 ) char ;

B:

实际数组B各元素的存储次序为:

LOC→

LOC是数组首地址

(该数组第一个元素的地址)

FORTRAN 例外,

它按列(优先)存放数组元素

*

北京航空航天大学计算机学院

1

9

a)n维数组的地址计算公式

设数组的维数为n, 各维的下界和上界为L(i)和U(i)

例如, 上例二维数组B

L(1) = 1 (隐含值), U(1) = N

L(2) = -2 ,

U(2) = 1

还假定n维数组元素的下标为V(1), V(2),…, V(n)

则该数组元素的地址计算公式为:

注:E为数组元素

大小(字节数)

n

ADR  LOC [V ( i )  L( i )]  P ( i )  E

i  1

其中

1

当i =n时

P(i) =

n

[

U( j) L( j) 1]

当1 ≤i <n 时

ji1

北京航空航天大学计算机学院

2

0

L(3)

U(3)

U(1)

v2

P(1)

v3

A(v1,v2,v3)

v1

L(2)

U(2)

[

V(1)-L(1) ]*[U(2)-L(2)+1]*[U(3)-L(3)+1]*E+

V(2)-L(2) ]*[U(3)-L(3)+1]*E+

V(3)-L(3) ]*1*E

[

[

L(1)

n

P(2)

ADR  LOC [V ( i )  L( i )]  P ( i )  E

i  1

其中

1

当i =n时

P(3)

P(i) =

n

[

U( j) L( j) 1]

当1 ≤i <n 时

ji1

北京航空航天大学计算机学院

2

1

(不变部分)

若令

n

RC   L(i) P(i) E

i1

n

则地址

ADR LOC RC V(i) P(i) E

i1

RC为数组元素地址计算公式中的不变部分。因为,只要知道

数组的维数和每一维的上下界值,便可求得RC值。

以前面所举的二维数组B为例,若N = 3

2

RC = L(i)P(i)* E

则 P(1) = [U(2) - L(2) +1]

1- (-2)+1

P(2) = 1

i1

=

=

=

-[ 1×4 +(-2)×1]×E

-2E

=

4

因此, 若有数组元素B( 2 , 1 ), 则它的地址为:

2

ADR LOC 2E  V(i) p(i) E =

LOC + 7×E

北京航空航天大学计算机学院

LOC – 2E+(2×4+1×1)×E

i1

2

2

L(3)

U(3)

U(1)

数组模板的一般形式如

下左图所示,而对于数组

B的模板如下右图所示:

v2

v3

A(v1,v2,v3)

v1

L(2)

U(2)

array B (3, -2: 1 ) char ;

1

-

2

L(1)

1

三维数组的例子

3

1

4

2

数组信息向量表

-

2

北京航空航天大学计算机学院

2

3

b) 数组信息向量表(模板)

U(n)

L(n)

P(n)

上界

下界

计算地址用

常量

功能:1、用于计算下标变量地址

2

、检查下标是否越界

.

..

.

...

一般形式:

U(1)

L(1)

P(1)

n

大小:3n + 2

RC

注:1、数组模板所需的空间大小取决于数组的维数,即3n+2

无论是常界或变界数组,在编译时就能确定数组模板的大小

、常界数组,在编译时就可造信息向量表;而变界数组信息向量

2

表要在目标程序运行时才能造。编译程序要生成相应的指令

北京航空航天大学计算机学院

2

4

以前面所举的二维数组B为例,若N = 3

P(2) = 1

数组信息向量表

P(1) = [U(2) - L(2) +1]

=

=

1- (-2)+1

4

1

U(2)--上界

L(2)--下界

-

2

1

3

1

4

2

P(2)--计算地址常量

U(1)--

L(1)--下界

2

RC = 

L(i)P(i)

上界

i1

P(1)--

计算地址常量

=

=

-[ 1×4 +(-2)×1]

n---维数

-2

-

2

RC

北京航空航天大学计算机学院

2

5

数组声明的ATG文法:

<

<

<

array del> →array @init <entity> ( <sublist> )

k

↑j

↑n

↑j

<

type> @symbinsert

t

↓j, n, t

sublist> →<subscript> @dimen#

j

↑j

|

<subscript>, <sublist> @dimen#

j

↑j

subscript> →<integer expr> @bannds

u

↓u

|

<integer expr> : @lowerbnd

l

↓l

<

integer expr> @upperbnd

u

↓u, l

1

) 动作程序@init 的功能为在分配给数组模板区中保留两个存

储单元,用来放RC 和n, 并将维数计数器j 清0。

2

) @dimen# : j := j + 1, 即统计维数

j

北京航空航天大学计算机学院

2

6

1

) @init:

p:= p+2;

j := 0; /*维数计数器*/

U(n)

L(n)

P(n)

.

..

活动

记录

数组

信息表

....

U(1)

L(1)

P(1)

n

RC

运行栈指针p

北京航空航天大学计算机学院

2

7

3

) @bannds将省略下界表达式情况的u => U(i),但应把相应的L(i)

置成隐含值1, 然后计算P(i )

实际P(i)计算公式可利用P(i)=[ U(i+ 1) - L(i +1) +1]×P(i+1)

1

当i =n 时

P(i) =

n

[

U( j) L( j) 1]

当1 ≤i <n 时

ji1

注:由于P(i)的计算要依赖于P(i +1 ), 所以实际P(i)的值是反填的

4

) @lowerbnd 把l => L(i )

@

upperbnd 把u => U(i ), 并计算P(i )

5

) 最后的动作程序@symbinsert是把数组名n,数组维数j 和数

元素类型t 及数组标志k 填入符号表中 ;为数组分配存储空

北京航空航天大学计算机学院

2

8

对于变界数组:

4

) @lowerbnd↓l

生成将l => L(i ) 的代码

upperbnd↓u

@

生成把u => U(i )的代码,

生成计算P(i )的代码;

生成将P(i )的值送模板区的代码;

5

) @symbinsert↓j, n, t

a) 把n, j, t, 填入符号表中

b) 生成调用运行子程序代码(计算RC, 并将计算结果和数组

名一起存入模板区;计算数组所需数据区大小,为数组分

配存储空间,并将头地址填入符号表。)

北京航空航天大学计算机学院

2

9

1

0.4 表达式的处理

分析表达式的主要目的是生成计算该表达式值的代码。通

常的做法是把表达式中的操作数装载(LOAD)到操作数栈

或运行栈)栈顶单元或某个寄存器中,然后执行表达式所指

定的操作,而操作的结果保留在栈顶或寄存器中。

注:操作数栈即操作栈,它可以和前述的运行栈

(动态存储分配)合而为一,也可单独设栈。

本章中所指的操作数栈实际应与动态运行(存储分配)栈分开。

请看下面的整型表达式ATG文法:

北京航空航天大学计算机学院

3

0

1

2

3

4

5

6

7

8

9

1

.<expression>→<expr>

.<expr>→<term><terms>

.<terms>→ε

procedure lookup(n);

string n; integer j;

j:= symblookup( n);

.

.

| +<term>@add<terms>

| - <term>@sub<terms>

/*名字n表项在符号表中的位置*/

if j < 1

then /*error*/

else return (j);

.<term>→<factor><factors>

.<factors>→ε

.

.

| *<factor>@mul<factors>

| /<factor>@div<factors>

end;

0.<factor>→<variable> @lookup

n

↓n↑j

procedure push(j);

@

push↓j

integer j;

1

1.

| <integer> @pushi

i

↓i

emit(‘LOD’, symbtbl (j).objaddr);

end;

12.

| (<expr>)

有关的语义动作为:

procedure pushi(i); /*压入整数*/

procedure add;

emit(‘ADD’);

end;

integer i;

emitl(‘LDC’, i) ;

end;

procedure mul;

emit(‘MUL’);

end;

北京航空航天大学计算机学院

3

1

对于输入表达式x + y * 3, 可以生成如下目标代码:

LOD, < ll, on> x

LOD, < ll, on> y

LDC, 3

MUL

ADD

上面所述的表达式处理实际上忽略了出现在表达式中各操

作数类型的不同,且变量也仅限于简单变量。

下面假定表达式中允许整型和实型混合运算,并允许在

表达式中出现下标变量(数组元素)。因此应该增加有关类

型一致性检查和类型转换的语义动作,也要相应产生计算下

标变量地址和取下标变量值的有关指令。

北京航空航天大学计算机学院

3

2

<

<

<

expression>→<expr>

t

expr> →<term> <terms>

t

↑s

↓s ↑t

terms> →@echo

s ↑u

↓s ↑u

|

|

+ <term> @add

<terms>

<terms>

t

↓t, s ↑v

- <term> @sub

↓t, s ↑v

↓v ↑u

↓v ↑u

t

<

term> →<factor> <factors>

u

↑s ↓s ↑u

<

factors> →@echo

s ↑u

↓s ↑u

|

|

*<factor> @mul

<factors>

↓t, s ↑v ↓v ↑u

t

/<factor> @div

<factors>

t

↓t, s ↑v

↓v ↑u

<

factor> →<variable> @type

t

↑i ↓i↑t

|

|

<integer> @pushi

i

↓i ↑t

<real> @pushi

r

↓r ↑t

<

variable> →<identifier> @lookup ↓n ↑j @push ↓j

j

↑n

|

<identifier> @lookup (@template ↓j ↑k<sublist> ↓k, j)

↓n ↑j

n

<

sublist> →<subscript> @offset ↓k, t ↑i <subscripts>

k, j ↑t ↓i, j

<

subscripts> →@checkdim

k, j

↓k, j

|

, <subscript> @offset ↓k, t ↑i <subscripts>

↓i, j

t

<

subscript> →<expr>

t

↑t

北京航空航天大学计算机学院

3

3

procedure offset( k, t );

integer k; string t;

k := k+1;

语义动作add等应作相应改变:

procedure add( t, s);

string t, s;

if t = ‘real ’ and s = ‘integer’

then begin

if t ≠ ‘ integer’

then errmsy(‘数组下标应为整

型表达式’, statno);

else emitl( ‘OFS’, k );

return (k);

emit( ‘CVN’); /*次栈顶转为实数*/

emit( ‘ADD’);

return ( ‘real’);

end;

procedure checkdim( k, j);

integer k, j;

end;

if k ≠ symbtbl (j).dim

then errmsy(‘数组维数与

声明不匹配’, statno);

else begin

if t = ‘integer ’ and s = ‘real’

then begin

emit( ‘CNV’); /*栈顶转为实数*/

emit( ‘ADD’);

return ( ‘real’);

emit( ‘ARR’);

emit( ‘DER’);

end;

end;

emit( ‘ADD’);

return ( t);

end;

procedure template(j);

integer j;

end;

emitl( ‘TMP’, symbtbl (j). objaddr);

k:= 0; /*维数计数器初始化*/

return(k);

北京航空航天大学计

3

4

end;

过程template发送一条目标机指令‘TMP’, 该指令把数组的模

板地址加载到操作数栈顶,并将下标(维数)计数器k清0。

★offset过程要确保每一个下标都是整型,而且发送一条‘OFS’

指令,该指令在运行时要完成以下功能:

1

2

. 检查第k个下标值是否在栈顶并是否在上下界范围内

.使用下列递归函数,计算地址计算公式中可变部分:

VP(0) = 0;

VP(k) = VP(k-1) + V(k) *P(k) 1≤k≤n

n

该VP函数是由计算公式

V (k) P(k)

 导出的

k 1

北京航空航天大学计算机学院

3

5

(d)

下面以数组元素B(2,1)为例,说明

B(2,1)值

a)执行TMP指令并形成第一个下标值的情况

b)执行第一 个OFS指令并形成第二个下标值的情况

c)执行第二个OFS指令及ARR指令后的情况

.

.

.

d)执行DER指令,最后在栈顶形成下标变量B(2,1)的值

数据栈

(c)

1

U(2)

L(2)

P(2)

U(1)

L(1)

P(1)

#dim

RC

(a)

V(2)→

1

-

2

8

+1=9

VP(2)→

1

3

1

4

2

LOC+(-2)+9

=LOC+7

locB

.

.

.

V(1)→

VP(0) →

2

0

locB

(b)

locB

栈底

-2

.

.

.

.

.

.

V(2)→

VP(1)→

1

2*4=8

locB

栈底

B的模板

.

.

.

操作数栈

栈底

北京航空航天大学计算机学院

3

6

处理逻辑表达式(关系表达式)的方法与处理算术表达式

的方式基本相同。下面是逻辑表达式~( x =y & y≠z | z<x)

生成的指令序列:

LOD, ( ll, on )x

LOD, ( ll, on )y

EQL

LOD, ( ll, on )y

LOD, (ll, on )z

NEQ

AND

LOD, ( ll, on )z

LOD, ( ll, on )x

LES

ORL

NOT

北京航空航天大学计算机学院

3

7

1

0.5 赋值语句的处理

X:= Y+X;

<

assignstat >→@setL ↑L<variable>↓L↑t:=

LDA ( ll, on )x

LOD ( ll, on )y

LOD ( ll, on )x

ADD

@resetL <expr> @storin ;

L

↑s

↓t,s

STN

@

setL是设置变量为“左值”(被赋变量),即将属性L置true

resetL是设置变量为非被赋变量,即把属性L置成false

@

procedure setL;

return (true );

end;

procedure resetL; procedure storin(t,s);

return (false );

end;

string t, s;

if t ≠ s

指示取变量地址 指示取变量之值

then /*生成进行类型转换的指令*/

emit(‘STN’);

北京航空航天大学计算机学院

end;

3

8

1

0.6 控制语句的处理

0.6.1 if语句

1

JMF, labA

IF <log_expr> THEN <stat_list> labA:

JMF, labA

IF <log_expr> THEN <stat_list> ELSE labA: <stat_list> labB:

JMP, labB

其属性翻译文法及相应的语义动作程序:

1

2

3

. <if_stat>→<if_head> <if_tail>

y

↓y

. <if_head> →IF <log_expr>@brf THEN <stat list>

y

↑y

. <if_tail> →@labprod

y

↓y

|

ELSE @br @labprod <stat_list>@labprod

z

↓y ↓z

39

动作程序@brf的功能是生成

JMF指令,并将转移标号返回给属性y

动作程序@br是是生成JMP指

令,并将转移标号返回给属性z

procedure brf;

string labx;

labx := genlab;

procedure br;

string labz;

labz := genlab;

emitl( ‘JMP’, labz);

return( labz);

end;

/*产生一标号赋给labx*/

emitl(‘JMF’, labx);

return (labx);

end;

动作程序@labprod是把从继承属

性y得到的标号设置到目标程序中

procedure labprod(y);

string y;

setlab(y);

/*在目标程序当前位置设标号*/

end;

北京航空航天大学计算机学院

4

0

1

0.6.4 for 循环语句

ATG文法

for 语句例子:

for i:= 1 to n by z do

<

statement>

..

end for;

.

1

2

.<for loop>→<for head>↑a, f, r < rest of loop>↓a, f, r

.<for head> →for <id> := <expr> @initload

a, f, r

↑a

↑s

to @labgen <expr> by

r

@

loadid <expr> @compare

a

↓a, s↑f

3

.<rest of loop> →do <stat list> end for

a, f, r

@

retbranch @labemit

r

↓f

@initload 只生成给循环变量赋初值的指令。

北京航空航天大学计算机学院

4

1

for <id> := <expr1> to <expr2> by <expr3> do <stat list>

LDA, (<id>)

LOD, (expr1)

STN

@

initload↑s

JMP, start

loop:

@labgen

r

LOD, (expr2)

@

loadid↓aLOD, (id)

LOD, (expr3)

ADD

STO, (id)

@

compare

a,s↑f

BGT, end_loop

start: <statement>

@

retbranch↓r

JMP, loop

labprod

end_ loop:

f

4

2

@

procedure compare( a, s);

address a; string f, s;

emit( ‘ADD’);

emitl( ‘STO’, a );

f := genlab;

procedure labgen

string r;

r := genlab;

setlab(r);

return ( r );

end;

emitl( ‘BGT’, f );

setlab( s );

return( f );

end;

procedure loadid( a )

address a;

emitl( ‘LOD’, a);

end;

procedure labprod( f ) // 即labemit

string f;

setlab( f );

end;

北京航空航天大学计算机学院

4

3

1

0.7 过程调用和返回

1

1

0.7.1 参数传递的基本形式

.传值(call by value) — 值调用

实现:

调用段(过程语句的目标程序段):

计算实参值=> 操作数栈栈顶

被调用段(过程说明的目标程序段):

从栈顶取得值=> 形参单元

过程体中对形参的处理:

对形参的访问等于对相应实参的访问

特点:

如C语言,

Ada语言的in参数,

Pascal 的值参数。

数据传递是单向的

北京航空航天大学计算机学院

4

4

2

.传地址(call by reference) — 引用调用

实现:

调用段:

如:FORTRAN,

Pascal 的变量形参。

计算实参地址=> 操作数栈栈顶

被调用段:

从栈顶取得地址=> 形参单元

过程体中对形参的处理:

通过对形参的间接访问来访问相应的实参

特点:

结果随时送回调用段

如:ALGOL 的

换名形参。

3

. 传名(call by name )

又称“名字调用”。即把实参名字传给形参。这样在过程体中引用形参

时,都相当于对当时实参变量的引用。

当实参变量为下标变量时,传名和传地址调用的效果可能会完全不同。

传名参数传递方式,实现比较复杂,其目标程序运行效率较低, 现已很少

采用。

北京航空航天大学计算机学院

4

5

1

0.7.2 过程调用处理

例:有过程调用:

process_symb(symb, cursor,replacestr);

与调用有关的动作如下:

调用该过程生成的目标代码为:

1. 检查该过程名是否已定

义(过程名和函数名不能

LOD, (addr of symb )

传值调用

LOD, (addr of cursor )

用错) 实参和形参在类型、 LOD, (addr of replacestr)

JSR, ( addr of process_symb)

顺序、个数上是否一致。

(查符号表)

<retaddr>:….

若实参并非上例中所示变量,

而是表达式,则应生成相应计算

实参表达式值的指令序列。

2

3

4

. 加载实参(值或地址)

. 加载返回地址

JSR指令先把返回地址压入操

作数栈,然后转到被调过程入口

地址。

.转入过程体入口地址

北京航空航天大学计算机学院

设过程说明的首部有如下形式:

procedure process_symb(string:symbal, int: cur, string: repl);

则过程体目标代码的 ALC, 4 + x /* x为定长项空间*/

STO, <actrec loc1> /* 保存返回地址*/

STO, <actrec loc4> /* 保存replacestr */

STO, <actrec loc3> /* 保存cursor */

STO, <actrec loc2> /* 保存symb */

开始处应生成以下指令,

以存储返回地址和形参

的值。

过程调用时,实参加载指令是把实参变量内容(或地址)

送入操作数栈顶,过程声明处理时,应先生成把操作数栈顶的实参

送运行栈AR中形参单元的指令。

将操作数栈顶单元内容存入运行栈(动态存储分配的数据区)

当前活动记录的形式参数单元。

可认为此时运行栈和操作数栈不是一个栈(分两个栈处理)

北京航空航天大学计算机学院

4

7

过程调用的ATG文法:

<

<

<

<

<

proc call>→<call head>↑i , z @initm↑m<args>

call head> →<id> @lookupproc

@genjsr↓i

↓i , z

i , z

↑n

↓n↑i, z

args> →@chklength | (<arg list>↓i, z)

↓i , z

i,z

arg list> →<expr> @chktype

<exprs>↓i , z

<exprs>↓i , z

i , z

↑t

↓t, i, m, z↑z

exprs> →@chklength

i , z

↓i , z

|

, <expr> @chktype

t

↓t, i, m, z↑z

procedure lookupproc(n);

string n; integer i, z;

i := lookup(n); /*查符号表*/

if i < 1

then begin

error( ‘过程’, n ,‘未定义’, statno);

errorrecovery( panic ); /*应急处理过程*/

return ( i := 0, z:= 0);

end

else return( i , z:= symtbl [i].dim); /* z为形参数目*/

end;

48

LOD, (addr of symb )

LOD, (addr of cursor )

LOD, (addr of replacestr)

JSR, ( addr of process_symb)

procedure chktype(t, i, m, z);

string t; integer m, i, z;

if z < 1

<retaddr>:….

then begin

error( ‘实参数大于形参数’, symtbl [i].name, statno);

return ( z);

end

m := m+1;

/* 实参计数*/

if t ≠symtbl [i+m].type

then error(‘实参和形参类型不匹配’, symtbl [i+m].name, statno);

z := z-1;

return (z);

/* 减去已匹配的形参数*/

/* 剩下待匹配的形参数*/

end;

@

@

chklenth 应检验z最后值为0。否则表示实参数目小于形参数目。

genjsr 生成JSR指令。该指令转移地址为symbtbl [i] .addr

北京航空航天大学计算机学院

4

9

过程说明(定义)的ATG文法如下:

<

proc defn>→<proc defn head> @initcnt↑j

parameters>↓j↑k @emitstores↓k

proc defn head>→procedure <id> @tblinsert

↓t, n

<

<

<

<

t

↑n

parameters> →@echo | (<parm list>↓j↑k)

↓j↑k

j↑k

parm list> →<type> : <id> @tblinsert

j↑l

↑t

↑n

↓t, n

@

upcnt↓j↑k <parms>↓k↑l

<

parms> →@echo

| , <type> : <id> @tblinsert

↓j↑l ↑t ↑n ↓t, n

j↑l

@

upcnt↓j↑k <parms>↓k↑l

@tblinsert 是把过程名和它的形参名填入符号表中:

procedure tblinsert( t, n );

string t, n; integer hloc;

if lookup ( n ) > 0

hashtbl[hloc] := s; /*s为符号表指针

(下标), 为全局量*/

symbtbl [s].name:= n;

symbtbl [s].type:= t;

s := s+1;

then error( ‘名字定义重复’, statno);

else begin

hloc := hashfctn(n); /*求散列函数值*/ end;

5

0

symbtbl

procedure emitstores(k);

integer k;

hashtbl

5

1

2

3

4

1

2

3

4

5

.

emitl( ‘ALC’, k + x +… );

emitl( ‘STO’, < ll, x+1 >);

s: 5 n

6

t

…← s

/

*保存返回地址*/

for i := k + x+1 down to x+2

*保存参数值*/

emitl( ‘STO’, < ll , i > )

end;

end;

.

.

.

.

.

.

.

.

.

.

.

/

m

ALC, 4 + x /* x为定长项空间*/

STO, <actrec loc1> /* 保存返回地址*/

STO, <actrec loc4> /* 保存replacestr */

STO, <actrec loc3> /* 保存cursor */

STO, <actrec loc2> /* 保存symb */

注:实际ALC指令所分配的空间应在

所有局部变量定义处理完以后,并考

虑固定空间(前述‘x’)大小,反填

回去。

北京航空航天大学计算机学院

5

1

1

0.7.3 返回语句和过程体结束的处理

其语义动作有:

1

) 若为函数过程,应将操作数栈(或运行栈)顶的函数

结果值送入(存回)函数值结果单元

2

3

) 生成无条件转移返回地址的指令(JMP RA)

) 产生删除运行栈中被调用过程活动记录的指令

只要根据DL—活动链,把abp退回去即可)

北京航空航天大学计算机学院

5

2

第十一章 代 码 优 化

概述

优化的例子

基本块的优化

循环优化

北京航空航天大学计算机学院

1

1

1.1 概述

代码优化(code optimization)

指编译程序为了生成高质量的目标程序而做的

各种加工和处理。

时间效率(减少运行时间)

目的:提高目标代码运行效率

空间效率(减少内存容量)

原则:进行优化必须严格遵循“不能改变原有程序语义”原则。

北京航空航天大学计算机学院

2

分类:

从优化的层次,与机器是否有关,分为:

独立于机器的优化:即与目标机无关的优化,通常是在中间代码上进

行的优化。

与机器有关的优化:充分利用系统资源,(指令系统,寄存器资源)。

从优化涉及的范围,又分为:

局部优化:是指在基本块内进行的优化。

循环优化:对循环语句所生成的中间代码序列上所进行的优化。

全局优化:顾名思义,跨越多个基本块的全局范围内的优化。因此

它是指在非线性程序段上(包括多个基本块, GOTO, 循

环)的优化。需要进行全局控制流和数据流分析,复杂。

北京航空航天大学计算机学院

3

[

定义] 基本块( basic block )

满足以下三个条件的程序段,称为基本块:

只有一个入口和一个出口,且语句为顺序执行的

程序段。

所有转移语句的目的语句都是基本块的第一条语

句。

转移语句的下一条语句是基本块的第一条语句。

如果块中任一语句被执行,则该块内的所有语句

也将被执行(无分支),且执行次数一样(无循

环)。

北京航空航天大学计算机学院

4

例:书上的例子

1. FACTOR = 2

基本块

基本块

1

2. EXP 1 =…

2

3

3. IF ( )GO TO 10

4. BASE = 2.0

4

5. FACTOR = FACTOR**2

1

0

6

. GO TO 20

0. BASE =…

5

11

6

1

基本块

基本块

2

0

1

11. FACTOR…

20. Q =

2

21. RETURN

先编号,后画图→决定基本块)

北京航空航天大学计算机学院

5

优化所花费的代价和优化产生的效果可用下图表示:

优化的

效果

局部优化 循环优化 全局优化

优化的

代价

北京航空航天大学计算机学院

6

图的左部表示只要做些简单的处理,便能得到明显

的优化效果。这相当于局部优化。

若要进一步提高优化效果,就要逐步付出更大的代

价——循环优化。

全局优化进一步提高。

北京航空航天大学计算机学院

7

为什么要优化?

有的大型计算程序一运行就要花上几十分钟,甚至好几

小时,这时为优化即使付出些代价也是值得的。

另外,程序中的循环往往要占用大量的计算时间。所以

为减少循环执行时间所进行的优化对减少整个程序的运

行时间有很大的意义。——尤其有实时要求的程序。如

市场决策,供需及求益的平衡

至于(像学生作业之类的)简单小程序(占机器内存,运行

速度均可接受),或在程序的调试阶段,花费许多代价

去进行一遍又一遍的优化就毫无必要了。

北京航空航天大学计算机学院

8

1

1.2 优化的基本方法和例子

注:实际的优化应在中间代码或目标代码上进行。但为

了便于说明,这里用源程序形式举例。

1)利用代数性质(代数变换)

编译时完成常量表达式的计算,整数类型与实型的转换。

例: a := 5+6+x → a := 11+x

又如: 设x为实型,x := 3+1 可变换成x := 4.0

• 下标变量引用时,其地址计算的一部分工作可在编译时

预先做好(运行时只需计算“可变部分”即可)。

北京航空航天大学计算机学院

9

• 运算强度削弱:用一种需要较少执行时间的运算代替另一

种运算,以减少运行时的运算强度时、空开销)

x**2 →x*x

3

8

*x →x+x+x

*x , 4*x 等换成左移运算

x/2 , x/16 等换成右移运算

x:=x+1 变为INC x指令

x/5 → x*0.2

利用机器硬件所提供的一些功能,如左移,右移操作,

利用它们做乘法或除法,具有更高的代码效率。

北京航空航天大学计算机学院

1

0

2) 复写(copy)传播

如x:=y这样的赋值语句称为复写语句。由于x和y值相同,所以当

满足一定条件时,在该赋值语句下面出现的x可用y来代替。

例如:

x:=y ;

u:=2*x ; → u:=2*y ;

v:=x+1 ; v:=y+1 ;

x:=y ;

这就是所谓的复写传播。(copy propagation )

若以后的语句中不再用到x时,则上面的x:=y可删去。

北京航空航天大学计算机学院

11

若上例中不是x := y而是x := 3。则复写传播变成了常量传播,即

u := 6;

v := 4;

x := y;

x := 3;

u := 2*x;

v := x+1;

u := 2*x;

v := x+1;

又如

t1 := y/z;

x := t1;

若这里t1为暂时(中间)变量,以后不再使用,则可变换为

x := y/z;

此外常量传播,引起常量计算,如:

pi = 3.14159

r = pi/180.0

此时:pi = 3.14159

r = 0.0174644 (常量计算)

北京航空航天大学计算机学院

1

2

3) 删除公共子表达式

具有相同值的子表达式在两个以上地方出现时,称它为

公共子表达式(common subexpression)

如赋值语句:

x := ( -y + z ) * z/2 - ( -y + z )

其中: (-y+z)是公共子表达式。

可用DAG (directed acyclic graph,有向无循环图)

来表示具有公共子表达式的抽象语法树。

北京航空航天大学计算机学院

1

3

x := (-y+z)*z/2-(-y+z)

:

=

x

显然,对于公共子表达

式只要计算1次即可

*

/

+

Y

Z

2

-

北京航空航天大学计算机学院

1

4

4) 删除冗余代码

冗余代码就是毫无实际意义的代码,又称死代码(dead code)或

无用代码(useless code)。

例如: x := x + 0 ;

x := x * 1 ; 等

又例:

FLAG := TRUE

IF FLAG

THEN…

FLAG永真

ELSE…

另外在程序中为了调试常有如下:

if debug then … 的语句。

但当debug为false时,then后面的语句便永远不会执行,

( 可用条件编译#if DEBUG 编写

程序,而源代码中还应留着)

这就是可删去的冗余代码。

北京航空航天大学计算机学院

1

5

5) 循环优化

经验规则告诉我们:“程序运行时间的80%是由仅占源程

序20%的部分执行的”。这20%的源程序就是循环部分,

特别是多重循环的最内层的循环部分。因为减少循环部

分的目标代码对提高整个程序的时间效率有很大作用。

for i = 1 to

for j = 1

10

to

100

x := x+0 ;

y := 5+7+x ;

优化一条,少10*100次运算

北京航空航天大学计算机学院

1

6

除了对循环体进行优化,还有专用于循环的优化

a) 循环不变式的代码外提

不变表达式:

不随循环控制变量改变而改变的表达式或子表达式。

如:FOR I := E STEP E TO E DO

1

2

3

BEGIN

不变表达式

可外提

S := 0.2*3.1416*R

P := 0.35*I

不能外提

V := S*P

北京航空航天大学计算机学院

1

7

while …

do

x := … (b*b - 4.0*a*c) …

若a,b,c的值在该循环中不改变时,则可将循环不变式

移到循环之外,即变为:

t1 := b*b - 4.0*a*c

while … do

x:= …( t1) …

从而减少计算次数——也称为频度削弱

北京航空航天大学计算机学院

1

8

b) 循环展开

循环展开是一种优化技术 。它将构成循环体的代码(不包括控制

循环的测试和转移部分),重复产生许多次(这可在编译时确

定),而不仅仅是一次,以空间换时间。

例 PL/1中的初始化循环 DO I = 1

TO

30

A[ I ] = 0.0

END

展开

I := 1

L1: IF I > 30 THEN 代码5条语句

A[1] = 0.0

30条语句

A[2] = 0.0 (指令)执行

GOTO L2

A[ I ] = 0.0

I = I+1

共执行5*30

……

也是30条语句

条语句

A[30] = 0.0

GOTO L1

1

9

L2:

循环一次执行5条语句才给一个变量赋初值。展开后,一条语句就能赋一

个值,运行效率高。

优化在生成代码时进行,并不是修改源程序。

必须知道循环的终值,初值及步长。

但并不是所有展开都是合适的。如上例中循环展开后节省执行了转移和

测试语句:2*30=60语句(其实,还不止节省60条)。

增加29条省60条

但若循环体中不是一条而是40条语句,则展开后将有40*30条=1200,但省

的仍是60条,就不算优化了。

判断准则:1. 主存资源丰富

循环展开有利

(大型机)

处理机时间昂贵

2. 循环体语句越少越好

北京航空航天大学计算机学院

2

0

实现步骤:

1

2

3

. 识别循环结构,确定循环的初值,终值和步长。

. 判断。以空间换时间是否合算来决定是否展开。

. 展开。重复产生循环体所需的代码个数。

比较复杂:

在对空间与时间进行权衡时,还可以考虑一种折衷的办法,即部分展开循

环。如上例展为:

DO I = 1 TO 30 BY 3

A[I] = 0.0

空间只多二条,

但省了20次测

试时间

A[I+1] = 0.0

A[I+2] = 0.0

(只循环10次)

END;

北京航空航天大学计算机学院

2

1

c) 归纳变量的优化和条件判断的替换

归纳变量(induction varable): 在每一次执行循环迭代的

过程中,若某变量的值固定增加(或减少)一个常量值,

则称该变量为归纳变量(induction variable)。即若当前

执行循环的第j次迭代。归纳变量的值应为c*j+c’, 这里c

和c’都循环不变式。

例: for i := 1 to 10

do

a[i] := b[i] + c[i]

北京航空航天大学计算机学院

2

2

for i:= 1

to

10 do

1

)

i := 1

a[i] := b[i] + c[i]

2

3

4

5

6

)

labb:

1

)

u := 4

)

if i > 10 goto labe

t1 := 4*i

2

3

4

5

)

labb:

)

)

)

)

if u >40 goto labe

tb := b [u]

t := b [ t ]

2

优化:

1

)

)

t3 := 4*i

7

)

t := c [ t ]

4

tc := c [u]

3

8

)

t := t + t

4

5

2

6)

t := tb + tc

a [ u] := t

t6 := 4*i

a[t6] := t5

i := i+1

7

8

9

1

)

7

8

9

1

)

)

u := u+4

)

)

goto

labb

)

goto labb

0)

labe :

3) labe:

中间变量t1 ,t3 ,t6 都是归纳变量

t1 := 4*i , t3 := 4*i , t6 := 4*i

d) 其它循环优化方法

把多重嵌套的循环变成单层循环。

把n个相同形式的循环合成一个循环等。

对于循环优化的效果是很明显的。某FORTRAN 77 编译程序,在

进行不同级别的优化后所得的目标代码指令数为:

优化级别 循环内的指令数(包括循环条件判断)

0

1

2

3

(不优化)

21

16

6

5

北京航空航天大学计算机学院

2

4

6)in_line 展开

把过程(或函数)调用改为in_line展开可节省许多处理过程

(函数)调用所花费的开销。

如:procedure m( i , j : integer ; max : integer);

begin if i > j then max:=i else max:=j end;

若有过程调用 m ( k , 0, max );

则内置展开后为:

if k > 0 then max := k else

max := 0;

省去了函数调用时参数压栈,保存返回地址等指令。

这也仅仅限于简单的函数。

北京航空航天大学计算机学院

2

5

7)其他, 如控制流方法

BR L

无条件转移

——为不可达代 码

L :

又如:转移到转移指令的指令

BR L1

BR L2

优化

L1: BR

L2

L1: BR

L2

北京航空航天大学计算机学院

2

6

还有:

BRCC

BR

L1

L2

当条件CC成立,转到L1

L1: ….

可改进为:

BR ’

L2

当条件不能成立时 ,转到L2

CC

(L1:) …

北京航空航天大学计算机学院

2

7

总结:

局部优化: (一个入口,一个出口,线性)——基本块

方法:常数合并

冗余子表达式的削除等

与机器无关的

优化,即独立

于机器的(中

间)代码优化

循环优化:对循环语句所生成的中间代码序列上所进行的

优化

方法:循环展开

优化

分为

两大

频度削弱

循环不变表达式的外提

强度削弱

全局优化:顾名思义,跨越多个基本块的全局范围内的优

化。因此它是在非线性程序段上(包括多个基

本块,GOTO循环)的优化。

与机器有关的

优化,即目标

代码上的优化

(与具体机器

有关)

北京航空航天大学计算机学院

2

8

第十二章 编译程序生成方法和工具

编译程序的书写语言

自编译性

自展

编译程序的移植

编译程序的自动生成

北京航空航天大学计算机学院

1

12.1 编译程序的书写语言

•机器语言或汇编语言

主要优点:编出来的程序效率高。

主要缺点:编程效率低,可读性差,不便于修改和移植。

高级程序设计语言已基本取代汇编语言

优点:编程效率高,可读性好,利于移植。

缺点:编译程运行效率较低。

北京航空航天大学计算机学院

2

12.2 自编译性

自编译性:如果一个高级语言能用来书写自己的编译程序,则

该语言具有自编译性,并称该语言为自编译语言。

两点说明:

1

. 通常用自编译语言除可编写本语言的编译程序以外,也可用

来编写别的语言的编译程序。

∴如果某台机器上已配备有某种自编译语言,则可利用这种

语言为本台机器配置其它的高级语言。

北京航空航天大学计算机学院

3

例:A机上有自编译语言L1的编译程序

L1 . AO

L ——语言L 的编译程序

1

1

AO——以A机的机器指令形式给出

利用语言L 可为A机生成语言L 的编译程序

1

2

L2. L1

L1.AO

L2.AO

L1源程序

L2可执行的编译程序

A机

L2源程序

L2.AO

L2目标程序

A机

可在A机上运行

北京航空航天大学计算机学院

4

源语

目标语

书写语

(d)程序

(b)解释程序

(c)计算机

(a)编译程序

北京航空航天大学计算机学院

5

L2

Ao

L1

L2

Ao

L1

Ao

Ao

用Ao描述的L2语

言的编译器L2.Ao

用L1写的L2语言

的编译器L2.L1

Ao

Ao

运行Ao的

计算机

用Ao描述的L1语

言的编译器L1.Ao

f

f

L2

Ao

L2

Ao

Ao

Ao

北京航空航天大学计算机学院

6

2

.自编译性不是绝对的,只是强弱不同

数据类型丰富的语言

自编译性强

控制结构丰富的语言

数据类型:除一般的外还有字符串类型,数组,结构,枚举,指

针等类型。

控制结构:应适于进行多分支的程序设计,如有CASE语句等

FORTRAN , ALGOL——自编译性差

PASCAL , C , ADA,C++,JAVA——自编译性强

实践示例:用PASCAL语言编写一个简单的编译程序,就是利用

PASCAL的自编译性。

北京航空航天大学计算机学院

7

12.3 自展

利用高级语言的自编译性,还可以通过自展方式生成语言的

编译程序。

设L为自编译语言,自展生成

L. Ao(A机目标形式的语言L的编译器,可在A机上运行)

步骤:1.首先,将语言划分为N个部分:

L = L + L +…+ L

n

1

2

L1 ——核心部分

L ~L ——扩充部分

2

n

北京航空航天大学计算机学院

8

2

3

.先用A机上的汇编编写L 的编译程序,L .Aa

1

1

L .Aa→Assember→L .Ao

1

1

.用L 编写L +L 的编译程序

1

1

2

(L +L ). L

L1.Ao

(L +L ).Ao

1

2

1

1

2

4

.用(L +L )编写L +L +L 的编译程序

1

2

1

2

3

5

.

L. (L +L …+L )

(L +L …+L ).Ao

L.Ao

1

2

n-1

1

2

n-1

北京航空航天大学计算机学院

9

L.Ao

滚雪球式

….

用自展方式进行编译,可提高生产率。因核心语言小,可用汇

编实现。其余部分高级语言编写。比全用低级语言效率高。

北京航空航天大学计算机学院

1

0

12.4 编译程序的移植

移植:将某台机上的成熟软件移植到另一台机器上,也就是将

宿主机上的软件移植到目标机上。如果使用具有自编译性的高

级语言来书写程序,则移植是方便的。

移植

L. L

L.Ao

宿主机A

目标机B

L.Bo

通过移植,在B机上可得到语言L

的编译程序,具B机目标形式,可

在B机上运行。

北京航空航天大学计算机学院

11

移植步骤:

. 将L. L分为两部分:

1

一部分与机器无关F.L 一部分与机器有关 A.L

L. L = F.L+ A.L

2

. 根据目标机用语言L改写与具体机器有关的部分:

L

A.L

B.L

产生A机代码 产生B机代码

∴交叉编译器: I.L= F.L+ B.L

用A机上的L语言所写的能生成B机目标代码的语言L的编译程序。

北京航空航天大学计算机学院

1

2

3

. 第一次编译

将I.L在宿主机A上用L的编译程序进行编译,生成能在宿主机A上

运行的语言L的交叉编译器,它能生成目标机B的代码。

I.L

L.Ao

I. Ao

语言L的交

叉编译器,

能在宿主机

A上运行,生

成目标机B的

代码

用L所写的生

成目标机B

代码的L语言

交叉编译器源

程序

宿主机A

的L编译

程序

北京航空航天大学计算机学院

1

3

4

. 第二次编译(交叉编译)

I.L

I. Ao

L.Bo

可在目标机B

上运行并生

成目标机B代

码的L编译

程序

A机

交叉编译程序:在宿主机A上运行,

但所生成的目标只能在目标机B

另一台机器)上运行。

北京航空航天大学计算机学院

1

4

L

Bo

L

Bo

L

Ao语言描述

的交叉编译

器I.Ao

L

Ao

Ao

L语言描述

的交叉编译

Ao

Ao

I.L

可在B机上运

行的L语言编

译器L.Bo

L

Bo

L

L

Bo

L

Bo

Bo

Ao语言描述

的交叉编译

Ao

L语言描述

的交叉编译

器I.L

Ao

器I.Ao

北京航空航天大学计算机学院

1

5

可以设想,只要在某台机器上为某目标机配置一个L语言的

交叉编译程序,就能将宿主机上的L语言所写的所有软件移植到

其他目标机上。

采用软件移植的办法来开发软件,可提高软件生产率,并提

高软件的可靠性。由于上述优点,所以软件的可移植性是软件开

发所追求的目标之一。

目前有许多编译程序都考虑到可移植性的要求。

例如有:可移植的PASCAL编译程序。

P.J.Brown , Software Portablility.

朱关铭等译,1982.12

北京航空航天大学计算机学院

1

6

12.5 编译程序的自动生成

理想的编译程序自动生成工具:

L源程序

该语言(L)

的编译程序

编译程序

生成工具

L语言规格说明

L语义描述和机器规格说明

目标代码

北京航空航天大学计算机学院

1

7

目前还没有一个系统能自动生成整个编译系统。

早期的工作集中在分析部分,即针对语法规则的形式化描述。

对编译程序后端,即与目标机有关的代码生成与代码优化部分,

由于对语义和目标机进行形式化描述方面所存在的困难,最近

有所突破,但未见到流行的产品。(样机——未形成真正产品)

有词法分析器的自动生成器和语法分析器的自动生成器。

词法分析器生成器(在第三章已作介绍)LEX:

LEX

词法分析器

lex.yy.c 文件)

(自动机)

LEX源文件(. l 文件)

构造识别单词符

号的自动机)

(正则表达式表示的单词符号)

北京航空航天大学计算机学院

1

8

语法分析器生成器:

YACC ( YET ANOTHER COMPILER - COMPILER )

YACC

.

y 文件

语法分析器

BNF表示的语

y.tab. c文件)

构造分析表和

控制执行程序 (LALR(1)分析器)

法描述可插入语

义动作)

Bison: 美国GNU开发的语法分析器生成器)和YACC一样都在

UNIX系统下运行。(已有PC版)

北京航空航天大学计算机学院

1

9

用yacc建立翻译程序

yacc源程序:translate.y

1

. 键入命令:

yacc translate.y

2

. 生成进行LALR分析的翻译

程序: y.tab.c

3

. 对生成的分析器进行编译:

cc y.tab.c -ly

(ly为使用LR分析器的库)

生成可执行的 翻译程序a.out

北京航空航天大学计算机学院

2

0

语法分析表

声明部分

头文件

y.tab.h

%%

yyparse()

{

语法分析函数

Yacc

翻译规则部分

编译程序

%

%

y.output

处理记录

函数定义部分

(拷贝)

函数定义部分

yacc源程序

y.tab.c

( *. y )

计算机学院

2

1

1

2

3

4

5

6

7

8

9

1

1

1

1

1

1

1

.

.

.

.

.

.

.

.

.

/* 表达式计算 */

%%

% token NUM

#include <ctype.h>

%%

yylex( )

{

line : expr ‘\n’

{ printf (‘\n’, $1);}

;

int c;

expr : expr ‘+’ term

{ $$ = $1 + $3; }

{ $$ = $1 - $3; }

/* $$ = $1 */

while (( c = getchar( )) == ‘ ’ );

| expr ‘-’ term

if ( isdigit ( c ) ) {

| term

;

yylval = c – ‘0’;

while ( isdigit( c = getchar ( ) ) )

yylval = yylval*10 + ( c-‘0’ );

ungetc ( c, stdin );

0. term : term ‘*’ factor

{ $$ = $1 * $3; }

{ $$ = $1 / $3; }

/* $$ = $1 */

1.

2.

3.

| term ‘/’ factor

| factor

;

retun NUM;

}

else return c;

4. factor: ‘(’ espr ‘)’

{ $$ = $2; }

/* $$ = $1 */

}

5.

6.

| NUM

;

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论