语法分析器是根据源语言的文法构造出来的。...

152
1

Upload: abie

Post on 22-Jan-2016

81 views

Category:

Documents


4 download

DESCRIPTION

语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。 语法分析方法:自顶向下分析:递归预测分析和 LL(1) 分析;自底向上分析:算符优先分析和 LR 分析 。. 4 . 1 语法分析器的作用 4 . 2预测分析器 4.2.1预测分析 4.2.2递归预测分析器的构造 4 .2.3非递归预测分析器的构造— LL(1) 4 . 3 书写文法 4 . 3 . 1消除左递归 4 . 3 . 1提取左因子 4.4自顶向下的带回溯的分析方法简介. 4.1语法分析器的作用. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

1

Page 2: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

2

语法分析器是根据源语言的文法构造出语法分析器是根据源语言的文法构造出来的。来的。

用于程序语言的编译程序构造中的一些用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器典型的语法分析方法及分析器的生成器YaccYacc。。

语法分析方法:自顶向下分析语法分析方法:自顶向下分析 :: 递归预测递归预测分析和分析和 LL(1)LL(1) 分析;自底向上分析分析;自底向上分析 :: 算符算符优先分析和优先分析和 LRLR 分析。分析。

Page 3: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

3

4.1 语法分析器的作用4.2 预测分析器 4.2.1 预测分析 4.2.2 递归预测分析器的构造 4 .2.3 非递归预测分析器的构造— LL(1)

4.3 书写文法 4.3 .1 消除左递归 4.3 .1 提取左因子4.4 自顶向下的带回溯的分析方法简介

Page 4: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

4

4.1 语法分析器的作用

词 法

分析器

语 法

分析器源程序

单词符号

取下一个单词符号

分析树

语 法

分析器w S w

* r m

* l m

语 法

分析器w S w

自顶向下分析 自底向上分析

Page 5: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

5

构造推导的关键问题

在构造最左推导的过程中 , 面对当前读入的单词符号和当前被替换的非终结符两者 , 应该选择这个非终结符的那条候选式去替换它( 推导 ); 主要找出选择一个非终结符号的候选式的方法 ;

在构造最右推导的过程中 , 面对当前读入的单词符号 , 已分析过的符号串中是否已构成一个产生式的右部(可归约),即句柄。如果已构成句柄 , 即用相应的产生式左部( 非终结符号 ) 去替换它 ( 归约 ) ,寻找句柄的方法。

Page 6: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

6

二 . 构造最左推导( aabbaa) (1. 自顶向下) SaASa A SbA SS ba

A

S

a S

bS A a

a b a

aSbAS

aabAS

aabbaS

aabbaa

aAS

S

Page 7: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

7

二 . 构造最右推导 (2. 自底向上) SaAS a A SbA SS ba

A

S

a S

bS A a

a b a

aAa ( a)

aSbAa (SbA)

aSbbaa (ba)

aabbaa (a)

aAS (aAS)

S

Page 8: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

8

4.2 预测分析器 4 .2 .1 预测分析 预测分析的原理 4 .2 .2 递归预测分析器的构造 用一组递归过程实现预测分析,产生式的状态转换图可作为编写递归过程的兰图。 4 .2 .3 非递归预测分析器的构造— LL(1)

对于每一步分析,分析表项M[A,a]=‘A’

表示:面对非终结符号 A 和向前看符号 a 应选择产生式 A 进行分析。

Page 9: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

9

4 .2 .1 预测分析

自顶向下分析是从文法的开始符号出发,试构造出一个最左推导,从左至右匹配输入的单词符号串。如果在每步推导中,面对被替换的非终结符号(不妨称为 A )和当前从左至右读入输入串读到的单词符号(又称向前看符号,不妨称为 a),如果 A 的定义(无 ε- 产生式) A→α1 |α2 |… |αn 中,只有αi( 1≤i≤n) 推导出的第一个终结符号是 a ,那么,我们就可以用产生式 A→αi 构造最左推导,即用 αi 替换 A。

Page 10: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

10

例 4.1 下面文法生成 pascal 类型的一个子集 type Simple

id (4.1)

array[ simple ]of type

Simple integer

char

num dotdot num

array [ num dotdot num ] of integer

Page 11: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

11

array [ num dotdot num ] of integer typearray[simple] of type { 注 :A=type,a=array, type→array[simple ] of type} array[num dotdot num] of type { 注 :A=simple, a=num simple→num dotdot num } array[num dotdot num] of simple { 注 :A=type,a=integer , type→simple } array[num dotdot num] of integer{ 注 :A=simple,a=integer ,simple→integer }

Page 12: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

12

例 4.1 ,在推导过程中,完全可以根据当前的向前看符号决定选择哪个产生式往下推导,因此,分析过程是完全确定的。这种分析称为自顶向下的预测分析。 原因在于,文法中, AVN,

A12... n

i,j(1i,jni<>j), 从 i 推导出来的第一个终结符号集合(称为 FIRST( i) )和从 j 推导出来的第一个终结符号集合(称为 FIRST(

j) )不相交,即, FIRST( i) FIRST(

j)=

Page 13: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

13

定义 4.1 令 G[S]=(VT,VN,S,P), 则

FIRST()=a a… a VT

例 4.2 文法 G[S] ,其产生式如下 :

S→aA|d A→bAS|ε ( 4.2)

若 w=abd ,则构造最左推导的过程如下 :

S aA { 注: A=S, a=a, S→aA}

abAS { 注: A=A, a=b, A→ bAS}

abS { 注: A=A, a=d , A→ε}

abd { 注: A=S, a=d , S→d}

*

Page 14: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

14

考虑第三步推导: abAS abS (A)

d 是句型 abAS 中紧跟随非终结符 A 的终结符号,把紧跟随非终结符 A 的终结符号集记作 Follow( A),其定义如下:

定义 4.2 令 G[S]=(VT,VN,S,P), 则

FOLLOW(A)=aS … Aa…,

a VT, A VN

若 S ...A, 则规定 $∈Follow(A) ,‘ $’作为输入串的结束符号( Follow(A) 的作用) 。

+

*+

+

Page 15: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

15

4.2.2 递归预测分析器的构造

文法( 4.1) 的预测分析器,由图 4.3 中所示的一组递归过程组成,这组递归过程的执行,将实现根据文法( 4.1) 对任一句子 w 进行预测分析。因此,这个分析器称为递归预测分析器。其中,过程 type 和过程 simple 是相应于文法( 4 . 1 )的非终结符号 type和simple 的 , 而过程 match 是为了简化过程type 和过程 simple 的代码所附设的。如果过程 match 的参数 t 与向前看符号 a 是匹配的,则进展到下一个输入符号。

Page 16: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

16

PROCEDURE match(t:token);

BEGIN

IF lookahead=t

THEN lookahead :=nexttoken

ELSE error

END;

Page 17: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

17

PROCEDURE type;

BEGIN

IF lookahead in{integer,char,num}

THEN simple ELSE

IF lookahead =‘’ THEN BEGIN

match(‘’); match(id) END ELSE

IF lookahead =array THEN BEGIN

match(array); match(‘[‘) ; simple;

match(‘]’); match(of); type END

ELSE error

END;

Page 18: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

18

PROCEDURE simple; BEGIN IF lookahead=integer THEN match(integer) ELSE IF lookahead=char THEN match(char) ELSE IF lookahead =num THEN BEGIN match(num); match(dotdot); match(num) END ELSE error END;

Page 19: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

19

构造方法: A VN, 构造一个递归程 , 不妨命名为 A 。过程 A 根据当前向前看符号 a处于 A 的哪个候选式的 FIRST 集合中,就选择那个候选式进行分析。根据选择的候选式,从左至右,若是终结符号,则和向前看符号匹配;若是非终结符号,则调用相应过程;若 aFOLLOW(A), 则选择 A。

使用状态转换图构造递归预测分析器

每个产生式右部是 VT V∪ N {∪ } 上的正规表达式,由此画出相应的识别器,称作状态转换图,以此作为编写递归预测分析器的蓝图。

Page 20: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

20

给定文法 G ,状态转换图的画法如下:

A VN

1. 创建一个初始状态和一个终结状态 ; 2 .对于每一个产生式 A X1X2,…, Xn,

创建一条从初始状态到终结状态的路径, 箭弧分别标记以 X1, X2 …, , Xn。例 4 . 3 (4.3) G[E]=( VT={+, * ,(,), id},

VN={E, E', T, T', F} ,E, P) P : E→TE' E'→+TE'ε T→FT' T'→*FT' ε F→(E) id

Page 21: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

21

0 1 2

3 4 5 6

7 8 9

10 11 12 13

14 15 16 17

E :

E’ :

T :

F :

T’ :

T

T

F

F

E

T’

T’

E’

E’

+

*

( )

id

图 4.4 文法 4.3 的转换图

Page 22: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

22

预测分析器的工作: 从开始符号的状态图开始,若处于状态s,从 s 有弧到达状态 t 。若当前向前看符号是a, a和某标记匹配,则分析进入 t 状态且读入下 一 个输入符号;若 aFIRST(A), 则转到 A 状态图,分析到达 A 状态图的接受状态后,分析进入 t状态;若 aFOLLOW(A), 则选弧。

状态图的确定性问题:

根据上述选择方法,若能选择唯一,则是确定的;否则,是非确定的。

Page 23: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

23

0 1 2

3 4 6

E :

E’ :

T

T

E’

图 4.5 简化转换图

5+ E’

3 4 5+ T

6

3 4+

T

6

0 3 4T

T

+

6

E : 0 3 6T

+

E’ :

Page 24: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

24

图 4.5 算术表达式的简化转换图

0 3 6E:T

+

7 8 13F*

T:

14 15 16 17F:E( )

id

Page 25: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

25

PROCEDURE E:

BEGIN T;

WHILE lookahead=‘+’ DO

BEGIN match(‘+’); T END

END;

PROCEDRUE T: BEGIN F; WHILE lookahead=‘*’ DO BEGIN match(‘*’); F END END;

Page 26: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

26

PROCEDURE F:

BEGIN

IF lookahead=‘(’

THEN BEGIN

match(‘(’); E; match(‘)’)

END

ELSE IF lookahead=id

THEN match(id)

ELSE error

END;

Page 27: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

27

作业:

4.1

4.2

4.3 (a) (c )

4.4 自己看

Page 28: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

28

4.2.3 非递归预测分析器的构造

一 . 基本思想

二 . 非递归预测分析器的模型和分析演示

三 . 非递归预测分析器的控制程序

四 . 计算 FIRST 和 FOLLOW 集合

五 . 非递归预测分析表的构造

六 . LL(1) 文法

七 . 在预测分析法中的错误处理示例

Page 29: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

29

一 . 基本思想

预测分析是在每步推导中 , 对被替换的非终结符号 A 和当前向前看符号 a 能选择A 的某条产生式进行推导。非递归预测分析的基本思想是,根据文法 G ,构造一张分析表 M ,表中元素M[A, a]存放的,要么是被选择的产生式(正确分析情况);要么是出错处理程序入口(分析出现错误)。整个分析是在分析表 M 的驱动下完成的。本节主要围绕 LL( 1 )分析表展开讨论。

Page 30: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

30

id + * ( ) $E E TE’ E TE’

E’ E’ +TE’ E’ E’

T T FT’ T FT’

T’ T’ T’ *FT’ T’ T’

F F id F id

id + id * id $输入

预测分析控制程序

$

EE’

TT’

Fid

E’

T

+

T’

Fid

ETE’

FT’E’

idT’E’

idE’

id+TE’

id+FT’E’

id+idT’E’

二 . 非递归预测分析器的模型

Page 31: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

31

三 . 非递归预测分析器的控制程序 置 ip指向 w$ 的第一个符号;令 x 为栈顶符号, a 是 ip 所指向的符号 . push(st,$);push(st,s);REPEAT IF xVT{$}

THEN IF x=a THEN { pop(st); ip:=ip+1} ELSE error( ) ELSE IF M[x,a]=xy1y2…yk

THEN {p0p(st);push(st,yk);…push(st,y1);

write(xy1y2…yk) }

ELSE error( ) UNTIL x=$

Page 32: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

32

四 . 计算 FIRST和 FOLLOW 集合

目的:构造文法 G 的分析表 M ,实际上,构造预测分析。 G[Z]: Z d XYZ Y c

X Y a (4.4)

(VTVN)*, =XYZ, X , Y , FIRST()必包含 FIRST(Z) 。同样, =AXYt

则 tFOLLOW(A) 。必须注意能产生空串的符号,这样的符号称为 nullable ,若 A ,则 nullable(A)=true。

*

*

*+

+

+

Page 33: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

33

算法 4.2 计算 nullable,FIRST和FOLLOW 初始化 : 除 FOLLOW(S)={$}外 , FIRST 和 FOLLOW 全为空集 . nullable 全为 false. FOR aVT DO FIRST(a)={a} ; REPEAT FOR A Y1Y2…Yn P DO { IF i(1i n) nullable(Yi)=true THEN nullable(A)=ture; FOR i:=1 TO n DO { IF (i=1) or (1 j i-1)nullable(Yj)=true THEN FIRST(A):=FIRST(A)FIRST(Yi); 计算 FOLLOW 集 }} UNTIL FIRST,FOLLOW,nullable 不再增大

Page 34: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

34

IF YiVN THEN

{ IF (i=n)orj(i+1j n)nullable(Yj)=true

THEN FOLLOW(Yi):=FOLLOW(Yi)

FOLLOW(A);

IF Yi+1 VT THEN Yi+1 FOLLOW(Yi)

ELSE

FOR k:=i+1 TO n DO

IF (k=i+1)or j(i+1j k-1)nullable(Yj)=true

THEN FOLLOW(Yi):=FOLLOW(Yi)

FIRST(Yk); }

Page 35: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

35

例 4.5 把算法 4.2 应用于文法 G[E]( 4.3) ,

得到下面的计算结果。nullable FIRST FOLLOW

E false (,id ),$

E’ true + ),$

T false (,id +,),$

T’ true * +,),$

F false (,id *,+,),$

VN

Page 36: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

36

五 . 非递归预测分析表的构造 在对文法 G 的每个非终结符号 A 及其任意候选 α都构造出 FIRST( α )和FOLLow( A )之后,用它们来构造 G 的分析表 M[A, a]。

算法 4 . 3 非递归预测分析表的构造 1 .对文法 G 的每个产生式 A 执行第 2步 和第 3 步; 2 . 对每个终结符号 a FIRST∈ (),把 A 加至 M[A,a] 中; 3. 若 α ε ,则对任何 b FOLLOW∈ ( A), 把 A 加至 M[A,b] 中 ; 4 . 把所有无定义的 M[A,a] 标上错误标记。

*

Page 37: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

37

六 . LL(1) 文法 对于某些文法, M[A,a] 可能含有多重定义。例 4.7 文法( 4.5), 它的分析表如表 4 . 4 所示。其中 M[ S’, e]包含两个产生式 S’eS 和 S’ 。这是来自文法( 4 . 5 )的二义 性 , FOLLOW( S’) ={e, $ } 反应了这一点。G[S]: S iEtSS’a S’ eS Eb (4.5)a b e i t $

S S a S iEtSS’

S’ S’ eS,S’ S’

E E b

Page 38: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

38

一个文法 G, 若它的分析表 M 不含多重定义入口 , 则被称为 LL(1) 文法。 LL(1) 中的第一个“ L”意味着自左而右地扫描输入 , 第二个“ L”意味着生成一个最左推导 ,并且“ 1”意味着为做出分析动作的决定,在每一步利用一个向前看符号 , 一个文法 G是 LL( 1 )的,当且仅当对于G 的每一个非终结符号 A 的任何两个不同产生式 Aαβ , 下面的条件成立 :1 . FIRST( α ∩) FIRST( β)= Φ ;它们不 应该都能推出空字 ε;2 .假若 β*ε, 那么 , FIRST( α ∩ ) FOLLOW( A)= Φ.

Page 39: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

39

七 . 在预测分析法中的错误处理示例 下面两种情况下发现源程序的语法错误: 1 .栈顶上的终结符号与下一个输入符 号不 匹配; 2. 分析表入口M[A,a] 为空。 处理方法:跳过剩余输入符号串中的若干符号,使栈和剩余输入符号串能重新协调地同步工作。在表中加入M[A,FOLLOW(A)]:=“synch” 。 在分析中查到入口M[A, a] ,若是空白,则跳过输入符号 a ;若是 synch ,则从栈中将非终结符号 A弹出。如果栈顶的终结符号与输入符号不匹配,则将此终结符号从栈中弹出。

Page 40: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

40

4.3 书写文法 通常使用的,不是 LL(1) 的,也不能用于递归预测分析器的构造。通过改写,使之适合于自顶向下分析。

4.3 .1 消除左递归 1 . 左递归的定义 2 . 左递归文法不适于构造自顶向下分析 3 . 消除直接左递归 4 . 消除间接左递归

4.3 .2 提取左公因子

Page 41: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

41

4.3 .1 消除左递归 1 . 左递归的定义 一个文法 G ,若存在推导 A A , 其中 , A V∈ N,∈ (VT V∪ N)* ,则称 G 是左递归的。 存在上述推导的原因是文法 G 中存在左递归规则。例如,描述表达式常用的语法G[E] ,其产生式如下 :

E→E+TT

T→T*F F ( 4.6)

F→(E) id

++

Page 42: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

42

二 . 左递归文法不适于用来构造自顶向下分析 这种规则更简单的代表是 S→Sa|b

( 1) FIRST( Sa ∩) FIRST( b ≠) Φ。 ( 2 )由 S 产生的句子是 {ban|n≥0} 。例如,对 于输入 baaa$ ,从左到右扫描输入串,开 始,向前看符号是 b ,此时,选用 S 的 哪 条候选式?若选用 S→b ,则肯定构造不 出 S baaa ;若选用 S→Sa, 则面对向前看 符号 b ,下次仍选用 S→Sa ,不知何时选 用 S→b 。构造分析树的过程如图 4.9 所示。

+

Page 43: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

43

S

S

S a

a

图 4.9 含直接左递归文法的分析树结构

用这样的规则构造递归预测分析的过程,那么,一进入这种过程不匹配任何输入符号,直接执行递归调用,形成自己调用自己的死循环。

Page 44: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

44

三 . 消除直接左递归 AA ,(VTVN)*

A A’ A’ A’ A *

文法( 4.6 ) 文法( 4.3)

EE+TT E TE’

T T*F F E’ +TE’

F (E) id T FT’

T’ *FT’

F (E) id

+

Page 45: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

45

直接左递归的一般形式

A A1 A2... An 1 2... m

A 1 A’ 2A’ ... mA’

A’ 1A’ 2A’ ... n A’

四 . 消除间接左递归 间接左递归的例子 S Aa b A Ac Sd (4.7) S Aa Sda 算法 4.4 要求无环路 A A 和无 - 产生式。

+

Page 46: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

46

算法 4.4 消除左递归1.AVN, 按任一顺序排列成 A1, A2, …, An.

2. for i:= 1 t0 n do

for j:=1 to i-l do

{ if Ajδ1|δ2|…|δk , Ai Ajγ P

then 把 Ai Ajγ改写成

Ai δ1γ|δ2γ|…|δkγ

消除关于 Ai- 产生式的直接左递归性 }

3 .化简由( 2 )所得到的文法。

Page 47: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

47

例 4.9 G[s]: S Acc A Bb b B Sa a 非终结符号排序为 B,A,S A (Sa a)b b 把 B 的产生式代入 A 中 A Sa bab b S (Sa bab b)c c 把 A 的产生式代入 S 中 S Sa bcabc bc c S abcS’ bc S’cS’ 消除直结左递归 S’ abcS’ A Sa bab b A 和 B 的产生式是多余的 B Sa a

Page 48: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

48

S Ac c A Bb b B Sa a 非终结符号排序为 S,A, B B ( Acc ) a a 把 S 的产生式代入B 中 B Acac a a B (Bbb)cacaa 把 A 的产生式代入 B 中 B Bbcabcacaa B bcaB’caB’aB’ 消除直结左递归 B’ bcaB’ S Ac c 最后 G[s] 的产生式 A Bb b B bcaB’caB’aB’ B’ bcaB’

Page 49: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

49

4.3.2 提取左因子 文法 G的产生式 A, 若 1 , 1 , 1 和 1 从左端开始有相同的子串,这个子串称为和的公共左因子。显然,含有公共左因子的规则 A 不能用来构造预测分析。为此,用提取左因子来改写这样的规则。例如, stmtif expr then stmt else stmt改写 if expr then stmt stmtif expr then stmt ( else stmt )成: stmtif expr then stmt S’ S’ else stmt

**

Page 50: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

50

若 A 1 2

改写成 : AA‘ A‘ 12

例 4.11 G[S]: S aSdAc A aS b (4.9) 把 A的产生式代入 S中: S aSdaScbc 提取左因子: S aSS’bc S’ dc A的产生式是多余的。 G[S]: SApBq A aAp d B aBq c (4.10)

Page 51: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

51

G[S]( 4.10 )在有限步骤内不能改写成无公共左因子的文法。

一般来说,程序语言的语法,经消除左递归和提取左因子后,能采用自顶向下的预测分析方法构造它的语法分析器。因为一般能被变换成 LL(1) 文法,只有个别结构是 LL(2) 文法。

对含有 LL(2) 语法结构的文法,采用递归的预测分析方法比 LL(1) 分析方法灵活,因为递归的预测分析方法在分析 LL(2) 结构时 ,能方便地向前多看一个符号,选取需要的产生式。

Page 52: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

52

4.4 自顶向下的带回溯的分析方法简介

并不是每个文法经改写后都可采用自顶向下的预测分析方法。例如 G[S](4.11), 这个文法产生偶数个 a的所有串 。 其产生式如下: S→aSa|aa (4.11) 面对 w ,扫描程序从左至右读 w ,即使往后多看 n个符号,一般来说也不知道选择哪个候选式能正确分析下去。不得已,只好采用试探法,不妨先选择最长的候选式进行推导,如不行,再选用另一个候选式。设w=aaaa$, 分析过程如下 :

Page 53: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

53

步骤 最左推导 和输入串匹配 第 1 步 SaSa w=aaaa$

第 2 步 aaSaa w=aaaa$

第 3 步 aaaSaaa w=aaaa$

第 4 步 aaaaSaaaa w=aaaa$ 至此,发现分析选用的候选式不对,回退到第 3步选用S→aa 进行分析。 第 5 步 aaaaaaaa w=aaaa$

第 6 步 aaaaaa w=aaaa$ 回退到第 2步

第 7 步 aaaaaa w=aaaa$ 回退到第 1步 成功

Page 54: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

54

上述分析称为带回溯的自顶向下分析。对于 w ,从文法的开始符号出发,反复使用不同的产生式谋求匹配输入串。当用某个非终结符号的候选式进行匹配失败时,删除这个失败分析建立的分析树分支并回头查输入符号,以便与其它候选式匹配。

为每个非终结符号设计一个递归布尔函数过程完成这种回溯分析。在进入过程未分析前,要保存当前分析格局,包括输入符号串指针,使用规则指针,分析树结构等。一旦发现它的某个候选式与输入串匹配,就用这个候选式去扩展分析树,并返回“真”值;否则,恢复进入过程时保存的分析格局,并返回“假”值。 另外,考虑加上语义分析,就会更麻烦,在保存和恢复现场格局方面,还要包括语义方面的工作,像符号表中的信息和生成代码的现场格局等。 这种带回溯的自顶向下分析技术,效率很低,代价极高,因此,它只有理论上的意义。

Page 55: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

55

作业:

 4. 6 试从文法 G[s]             S→(L)|a                L→ L, S|S 中消除左递归。并为之构造一个递归预测分析器和 LL(1) 分析表。请说明句子 (a,(a, a))在 LL(1) 分析器上的的动作。(参考129页表 4 . 3 ) 4. 9 对于文法 G[bexpr]            bexpr→bexpr or bterm | bterm             bterm→bterm and bfactor | bfactor             bfactor→not bfactor|(bexpr) |true |false      构造一个递归预测分析器。

    

Page 56: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

56

    4.8 (a) 计算下面文法 G[S]的 nullable, FIRST 和 FOLLOW 集 S→uBDz              B→Bv|w             D→EF              E→y |ε              F→x |ε      (b) 构造这个文法的 LL(1) 分析表。 ( c ) 给出这个文法不是 LL(1) 的证据。 (d) 尽可能少的修改这个文法,使其成为 能产生相同语言的 LL(1) 文法。

Page 57: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

57

4.5 自底向上分析 把一个输入符号串逐步归约到文法的开始符号。 这种方法的大致过程是,用一个栈,把输入符号一个一个地移进到栈里,当栈顶形成某个产生式的右部( 句柄)时,把栈顶的这一部分替换成 ( 归约为 )它的左部符号。称作“移进—归约”分析。 4.5.1 规范归约 句柄

4.5.2 “移进—归约”分析的栈实现

Page 58: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

58

4.5.1 规范归约 归约

G=(VT,VN,S,P),α, β ∈(VT∪VN)*,A→β∈P,αAw αβw 。归约的过程是,已知 αβw和产生式 A→β ,用产生式 A→β 左部 A 替换 αβw中的 β ,得到符号串 αAw  。 从输入符号串出发 ,每次从被归约的句型中找到一个产生式的右部 ,用其左部替换之,得到新的句型 ,直至归约到文法的开始符号。 因为从左至右读入输入符号串,自然在被归约的句型中找最左边的某个产生式的右部(句柄)进行归约。

Page 59: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

59

例 4.12 G[S](4.12), 其产生式如下: ① S→aABe ②A→b     ③A→Abc   ④B→d  (4.12) 输入串 abbcde aAbcde aAde aABe S              SaABe aAde aAbcde abbcde

a b b c d e abbcde

A aAbcde

AaAde

B

S

aABe

S

Page 60: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

60

例 4.12 G[S](4.12), 其产生式如下: ① S→aABe ②A→b     ③A→Abc   ④B→d  (4.12) 输入串 abbcde aAbcde aAde aABe S              SaABe aAde aAbcde abbcde

a b b c d e abbcde

A aAbcde

AaAde

B

S

aABe

S

Page 61: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

62

S

A

w

Aw w

已知 w ,分析已识别出,产生式 A 的右部,再看待输入串 w 的最左边符号,用 A 替换得到 Aw; 自上而下分析是从 w A ,看FIRST(), w A w

Page 62: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

63

定义 4.3 假定是文法 G 的一个句子。称右句型序列 n , n-1,…, 1, 0 是 的一个规范归约,如果序列满足 1. n= , 0=S ;     2.i(0 ≤ i < n    ), i i+1

规范归约是关于的一个最右推导的逆过程。如果文法 G 是无二义的,那么,规范推导 ( 最右推导 )的逆过程必是规范归约 ( 最左归约 )。 βw 表示一个规范句型 , 是在 β 归约之前进行的规范归约得到的结果 , (VTVN)* ,w VT

* 。句柄的“最左”特征使得在移进 -归约方法中,它处于符号栈的栈顶。

rm

Page 63: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

64

二义性文法存在规范归约不唯一的句子。例如,文法 G[E]:

  E→E+ E E*E (E) id 句子 id+ id*id 有二个不同的最右推导 :      EE+E   EE*E E+ E*E E *id          E+ E*id3 E+E*id         E+id2*id3 E+id*id         id1+id2*id3 id1+id2*id3 句型 E+ E*id3 中 ,句柄不唯一 。 规范归约的中心问题是:如何寻找或确定 一 个句型的句柄 。

Page 64: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

65

4.5.2 “ 移进 - 归约”分析法的栈实现

“移进一归约”分析器使用一个栈和一个存放输入符号串 w的缓冲器。分析器的初始状态为 : 栈 输入 $ w $     工作过程:自左至右把串 w 的符号一一移进栈里,一旦栈顶形成句柄时,就进行归约。这种归约可能持续多次,直至栈顶不再呈现句柄为止。然后,继续向栈里移进符号,重复这个过程,直至最终形成如下格局: 栈 输入 $ S         $

   

Page 65: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

66

G[s]: SaAcBe A bAb B d

步骤 栈 输入 动作(1) $ abbcde $ 移进(2) $a bbcde$ 移进(3) $ab bcde$ 归约,A b(4) $aA bcde$ 移进(5) $aAb cde$ 归约,A Ab(6) $aA cde$ 移进(7) $aAc de$ 移进(8) $aAcd e$ 归约,B d(9) $aAcB e$ 移进(10) $aAcBe $ 归约,S aAcBe(11) $S $ 接受

Page 66: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

67

“移进 -归约”分析对符号栈的使用有四类操作:移进、归约、接受和出错处理。规范句型 ( 右句型 )的“活前缀” , 定义如下 :定义 4 . 4 一个规范句型的一个前缀,若不含句柄之后的任何符号 ,则称它为该规范句型的一个活前缀。 分析过程的每一步骤,栈里的文法符号串加上剩余输入符号串恰好是一个规范句型。而且栈里的文法符号串正好是这个句型的一个活前缀。如在表 4.7(a) 的前三步中可以看到, a及 ab都是符号串 abbcde 的活前缀。 “移进 -归约”分析识别规范句型的活前缀。

Page 67: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

68

4.6 算符优先分析法 概述 一 . 算符文法的定义 二 . 算符优先分析法的基本思想4.6.1 利用算符优先关系寻找右句型的可归 约串

4.6.2 算符优先关系表的构造

4.6.3 优先函数

总结

Page 68: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

69

一 . 算符文法的定义 定义 4.5 设 G 是一个文法 ,如果 G 中不存在形如 A及 A→BC 的产生式 ( 其中A,B,CVN ,, (VN VT)*且其中不含有相邻非终结符号) ,即 G 中没有右部为或右部具有相邻非终结符号的产生式 ,则称 G 为算符文法。 G[E]: E→E+ E|E-E|E*E|E/E|EE|(E)|-E|id        是算符文法。 (4.13)      E→EAE|(E)|-E|id      A→+ |- |*|/| 不是算符文法。因右部 EAE具有相邻的非终结符号。

Page 69: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

70

二 . 算符优先分析的基本思路 由于文法 (4. 13)是一个二义文法,它的句子往往有不同的规范推导,按传统的习惯规定优先级从高到低为:乘幂运算符,乘、除运算符,加、减运算符;同级运算符服从左结合原则;有括号时,先括号内后括号外。 文法的句子 id+ id- id*(id+ id) 的归约过程为: (1)id+ id- id*〔 id+ id )     (2) E+ id- id*(id+ id )     (3) E+ E- id*(id+ id )     (4) E- id*(id+ id )     (5) E-E*(id+ id)     (6) E-E*(E+ id)

Page 70: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

71

(7) E-E*(E+ E)      (8) E- E*(E)     (9) E- E* E (10) E- E  (11) E

  这个归约过程是唯一的 。上述归约过程中起决定作用的是相邻两个终结符号之间的优先关系 。一旦确定了这种优先关系,就可以借助这种关系去寻找可归约串并进行归约。 终结符号 a与 b 之间的优先关系有三种: a b 表示 a的优先级低于 b     a b 表示 a的优先级等于 b     a b 表示 a的优先级大于 b

Page 71: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

72

注意:

1. 算术关系“ <”,“=” 和“ >” 与优先关系具有十分不同的性质。例如, a< ·b 并不一定意味着 b·>a ,例如: + <· (,( <· + 。 2. 决定优先关系方法:

( a ) 直观方法:代数规则; ( b )对于一个无二义性文法,有机械方法。

Page 72: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

73

4.6.1 利用算符优先关系寻找右句型的可归约串 算符文法右句型的形式为(可以证明) β0a1β1a2β2…anβn 其中,  βiVN{}, an VT 。假设在 ai和ai+1之间三个关系 <·,=, ·> 中至多有一个成立。进而,$作为每一个右句型符号串的左右分界符,算符文法右句型的形式为: $ β0a1β1a2β2…anβn $并规定, ai, $ <· ai, ai ·> $。 在句型中加入优先关系,例如: id+id*id $ <·id ·> + <·id ·> * <·id ·> $句型中 <· 和 ·>之间的符号串是待归约的符号串。

.

Page 73: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

74

找右句型的可归约串 的方法 $ <·id ·> + <·id ·> * <·id ·> $ 1· 找可归约串 的右端; 2 · 找可归约串 的左端; 3 · 归约 使用下面的优先关系表,分析过程如下:

id + * $id · · ·+ · · · ·* · · · ·$ · · ·

Page 74: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

75

栈 关系 输入 动作$ <· id+id*id$ 移进 $ id ·> +id*id$ 归约 $ E < · +id*id$ 移进$ E+ < · id*id$ 移进$ E+id · > *id$ 归约$ E+E < · *id$ 移进$ E+E * < · id$ 移进$ E+E * id · > $ 归约$ E+E * E · > $ 归约$ E+E · > $ 归约$ E $ 接受

Page 75: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

76

算法 4 . 5 算符优先分析法 方法: if (ab) or (ab) then begin /* 移进 * / 把 b 推入栈中; 使 ip前进到下一个符号; end if a·b then /* 归约 * /  repeat              从栈中弹出符号    until 栈顶终结符号最近弹出的终结 else error 符号

Page 76: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

77

算法中,每一个归约串中至少包含一个终结符号,用到了一个重要的概念和结论。 定义 4 . 5 设 G 是一个算符文法, β 是句型δ 关于 A 的短语 ( 即有 S αAδ且 A β )且 β 至少含有一个终结符号,并且除自身之外不再含有任何更小的 带有终结符号的短语,则称 β 是句型 αβδ 关于 A 的素短语。所谓最左素短语是指处于句型最左边的那个素短语。 设 G 是一个算符文法,如果 G 中任何两个终结符号之间至多有一种优先关系存在,则是一个算符优先文法。 算符优先文法句型的最左素短语是唯一的。

* +

Page 77: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

78

句柄和素短语的区别:G[E]:EE+TT E E+E E*E (E) id T T*FF F (E) id

E

E + T

* FT

F id

id

T

F

id

E

E + E

*E E

id id

id

Page 78: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

79

4.6.2 算符优先关系表的构造

一 .直观方法:代数规则

( 1) id 是最基本的运算量

( 2 )一目运算符号减,例如,

-id-id

( 3)是右结合。

二 .形式方法 ( 本节总结时有简单介绍)

Page 79: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

80

+ - * / id ( ) $+ - * / id ( ) $

表 4.9 优先关系表

Page 80: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

81

4.6.3 优先函数 为了节约存储空间和便于执行比较运算 ,用两个优先函数 f和 g ,它们是从终结符号映射到整数的函数。对于终结符号 a和 b选择 f和 g,使之满足: 1.当 a< ·b 时 , f(a)< g(b);     2. 当 a = b时 , f(a)= g(b);    3. 当 a ·> b 时 , f(a) > g(b)。   于是 a和 b 之间的优先关系可以由比较f(a) 与 g(b) 的大小来决定。 损失 :错误检测能力降低,例如, id ·>id 不存在 , f(id) >g(id) 可比较。

·

Page 81: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

82

表 4.9 对应的优先函数:

+ - * / ( ) id $

f 2 2 4 4 4 0 6 6 0

g 1 1 3 3 5 5 0 5 0

1) 构造优先函数的算法不是唯一的。

2 ) 存在一组优先函数,那就存在无穷组优先函数。

Page 82: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

83

算法 4.5 从优先关系构造优先函数

方法: 1.aVT{$}, 建立两个符号 fa和ga;

2. 若 a = b, 则把 fa和 gb 分在一组; 3 . a, b VT,

若 a b ,则从 fa至 gb 画一条弧; 若 a b ,则从 gb至 fa 画一条弧 ;

4 . 若图中无环,则存在优先函数, f(a)和 g(b)等于从 fa 和 gb 出发的 最长路径。

.

Page 83: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

84

id + * $id + < <

* <

$ < < <

gid fid

f* g*

g+ f+

f$ g$

id + * $

f 4 2 4 0

g 5 1 3 0

Page 84: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

85

总结: 1. 算符优先分析法能方便地构造表达式的语法分析器,分析速度也比较快; 2. 诊查错误的能力较弱,适用的范围小; 3. 形式化方法求优先关系简介

优先关系定义: 设是 G 不含 - 产生式的算符文法, a,bVT,

1) a = b A…ab... P 或 A…aQb... P

.

Page 85: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

86

2) a < b A…aR... P

且 (R b… 或 R Qb… )

3) a >b A…Rb... P

且 (R …a 或 R …aQ )

a < FIRSTVT(R); LASTVT(R) >b

+

+

+

+

G[E]: EE+TT T T*FF F (E) id + < FIRSTVT(T)

LASTVT(T) >*

E

E + T

*T F

idid

id

Page 86: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

87

作业: 4.14 4.15 4.16(b)

Page 87: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

88

4.7 LR 分析器

4.7 .1 LR 分析器的逻辑结构及工作过程

4.7.2 SLR 分析表的构造

4.7.3 LR ( 1 )分析表的构造

4.7.4 LALR(1) 分析表的构造

4.8 LR 分析对二义文法的应用

4.9 分析器的生成器 yacc

Page 88: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

89

LR(k) 分析技术。这里的“ L” 是指从左至右扫描输入符号串,“ R” 是指构造一个最右推导的逆过程,“ k” 是指为了作出分析决定而向前看的输入符号的个数。

LR 分析方法是当前最广义的无回溯的“移进 - 归约”方法。根据栈中的符号串和向右顺序查看输入串的 k(k0) 个符号,就能唯一确定分析器的动作是移进还是归约,以及用哪个产生式进行归约。 LR(k) 分析技术 knuth 于 1965年首先提出

Page 89: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

90

来的。

优点:适用范围广;分析速度快;报错准确。

构造分析器的工作量很大,不大可能手工构造;用软件工具 yacc-Yet Another Compiler Compiler,Bell,1974. LR(0) SLR(1) LR(1) LALR(1)

Page 90: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

91

4.7.1 LR 分析器的逻辑结构及工作过程 一个输入、一个输出、一个栈、一个驱动程序和一张分析表

id+id*id$

Sm

Xm

Sm-1

Xm-1

…S0

LR驱动程序

动作 转移action goto

输出

Page 91: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

92

分析表: 移进 ai 和 s=goto[sm,ai] 进栈action[sm,ai]= 归约 rj : AXm-r+1Xm-r+2…Xm

接受 s=goto[sm-r , A]

出错G[E’]:

(0) E’ E (1) E E +T

(2) E T (3) T T*F

(4) T F (5) F (E)

(6) F id

Page 92: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

93

表 4.11 文法( 4.22 )的 SLR 分析表 id + * ( ) $ E T F

0 s5 s4 1 2 31 s6 acc2 r2 s7 r2 r23 r4 r4 r4 r44 s5 s4 8 2 35 r6 r6 r6 r66 s5 s4 9 37 s5 s4 108 s6 s49 r1 s7 r1 r1

10 r3 r3 r3 r311 r5 r5 r5 r5

Page 93: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

94

LR 分析器的工作过程

格局:(栈中符号序列,剩余输入符号串)

开始:( s0, a1 a2 ……an$)

中间: ( s0x1s1x2s2…xmsm ,aiai+1…an$)

x1x2…xmaiai+1…an 是一个右句型。

1. 若 action[sm.ai]=si ,

则把 ai,si=action[sm ,ai] 推进栈 .

格局:( s0x1s1x2s2…xmsm aisi , ai+1…an$)

Page 94: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

95

2. 若 action[sm.ai]=r (Axm-r+1xm-r+2…xm) , 则

格局:( s0x1s1x2s2…xm-rsm-r As , ai ai+1…an$)

其中, s=goto[sm-r ,A]

3.若 action[sm.$]=accep, 则分析结束。

4 .若 action[sm,ai]=error, 则转出错处理程序。

下面,显示 id+id*id的 LR 分析过程:

Page 95: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

96

栈 输入 动作0 id+id*id$ s5 0id 5 +id*id$ r6 Fid 0F3 +id*id$ r4 TF

0T2 + id*id$ r2 ET

0 E1 +id *id$ s6

0E1+6 id *id$ s5

0E1+6 id5 *id$ r6 Fid

0 E1+6 F3 *id $ r4 TF

0E1+6T9 *id $ s7

0E1+6T9*7 id $ s5

0E1 +6T9*7 id5 $ r6 Fid

Page 96: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

97

栈 输入 动作0E 1+6T9*7 F10 $ r3 TT*F

0E1+6T9 $ r1 EE+T 0E1 $ accep

0 5

3

2

1 6 9 7 10

id

F

T

E + T * F

id idF

分析表 4.11 是识别文法 G[E]活前缀的有限自动机。

Page 97: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

98

4.7.2 SLR 分析表的构造

根椐文法 G ,构造识别文法 G 的所有活前缀的 DFA m ,根椐 DFA m 构造分析表。分析表是 DFA m 的一种描述形式。

一 . 项目

识别活前缀的 DFA m 每个状态是一个项目集。

S A , VT

活前缀: 的前缀是右句型 的活前缀。

*rm rm

Page 98: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

99

活前缀和句柄的关系:1. 活前缀不含有句柄的任何符号, ;2. 活前缀含有句柄的部分符号, 1 ;3. 活前缀已含有句柄的全部符号, 。 识别活前缀的自动机处于的格局:

0

q0 q1

q

0 q q2

1

Page 99: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

100

用圆点“”表示识别一个产生式右部符号到达的位子,若有规则 AXYZ, 则有下面四个项目: A XYZ A a,aVT 移进项目 A X YZ A B,BVN

A X Y Z 待归约项目 A X YZ A, A A 归约项目 S´S, S´S 接受项目以上项目称作 LR(0) 项目。

Page 100: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

101

二 . 有效项目集和转移函数

定义 4.6( 识别活前缀的有效项目 ) 如果存在一个规范推导

S αAw αβ1β2w 

项目 A→β1·β2 对识别活前缀 γ=αβ1 是有效的。

有下面的结论: 若项目 A→α·Bβ 对识别活前缀 =δα 是有效的且若 B→ηP ,则项目 B→·η 对识别活前缀 =δα也是有效的。

*rm

rm

Page 101: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

102

推导如下:

若项目 A→α·Bβ 对识别活前缀 =δα 是有效,则存在一个规范推导

S δAw δαBβw 

设 βw xw , 则对任何 B→ηP ,有

S δAw δαBβw

δαBxw δα η xw

则 B→·η 对识别活前缀 =δα 也是有效的。

A→α·Bβ 和 B→·η 在同一个项目集中。

*rm

rm

*rm

*rm

rm

*rm

rm

Page 102: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

103

识别文法 G 的某个活前缀 γ 的所有有效项目组成的集合称为 γ 的有效项目集。文法 G 的所有有效项目集组成的集合称为 G 的 LR(0) 项目集规范族。 定义 4.8 设 I是文法 G 的一个 LR(0) 项目集合 , closure(I) 是从 I出发用下面三个规则构造的项目集 : 1. I中每一个项目都属于 closure(I) 。     2 .若项目 A→α·Bβ closure(I)且B→ηP 则将 B- ·η加进 closure(I) 中。 3.重复执行 (2) 直到 closure(I) 不再增大为止。 显然对任何有效项目集 I,都有 I= closure(I) 。

Page 103: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

104

定义 4.8 ( 转移函数 )

若 I是 G 的一个 LR(0) 项目集 , X  {VTVN}  

  go(I,X)=closure(J) 其中 ,   J={A→αX·β| 当 A→α·Xβ I时 }  go(I,X) 称为转移函数。项目 A→αX·β 称为A→α·Xβ 的后继。

I: A→α·Xβ J: A→αX·βX

I 中项目 A→α·Xβ 是识别某个活前缀 γ=δα 的有效项目,则有规范推导:

Page 104: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

105

S Aw Xw

J中项目 A→αX·β 是活前缀 γX 的有效项目。 算法 4.8 计算 closure(I)和 go(I,x)  FUNCTION closure(I:SET OF item):SET OF item;     BEGIN       REPEAT         FOR A→αBβ I           FOR B→ η P             I:=I∪{B→ η}       UNTIL I 不再增大 ;       Return (I)     END; {A→αBβ 是项目集 I的核项目 }

*rm rm

Page 105: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

106

FUNCTION go(I:SET OF item; x∈{VT∪VN})

:SET OF item;       VAR J:SET OF item;      BEGIN        J:={ };        FOR A→αXβ I          J:=J∪{A → αX β}        Return (closure(J))      END;

Page 106: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

107

LR(0) 项目集规范族的构造 PROCEDURE items(G ); BEGIN C:={closure( S → S) }; REPEAT FOR IC 和 X {VT V∪ N} 把 go(I,X) 加入到 C中 UNTIL C 不在增大 END; DFA m= (VT V∪ N {S ∪ }, Q{ 项目集规范族 }, q0= closure{S → S},Q,=go(I,X) )它识别文法 G 的所有活前缀。

Page 107: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

108

三 . 例示 LR(0) 分析表的构造 例 4.17 ( 0) S´S (1) S aA (2) S bB (3) A cA (4) A d (5) B cB (6) B d

I0

S ´SS aAS bB

aS aAA cAA d

S ´SS

b

S bBB cBB d

c A c AA cAA d

c

图 4.17

A d

dd

I1

I2

I3

I4

I10

Page 108: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

109

根据图 4.17的 DFAm ,其 LR(0) 分析表如下:

a b c d $ S A B

0 S2 S3 1

1 acc

2 S4 S10 6

3 S5 S11 7

10 r4 r4 r4 r4 r4

Page 109: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

110

结论 : 对于拓广文法 G 的每一个活前缀 ,识别它的有效项目集恰好是从它的识别活前缀的 DFA 的初态出发经过 道路所到达的那个状态所代表的项目集。事实上,在任何时候分析栈中的活前缀 X1X2…Xm 的有效项目集恰恰是栈顶状态 Sm 所代表的那个集合。这也表示栈顶状态体现了栈里一切有用的信息。 对同一个活前缀存在若干不同的项目对它是有效的,而且它告诉我们应做的事情可能各不相同,互相冲突。这种冲突通过向前看几个输入符号或许能够解决。

Page 110: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

111

四 . SLR 分析表的构造 有的文法,识别它的活前缀的 DFA 的状态集中,有的状态的项目集中含有冲突项目。

例: (0) E ´E (1) E E+T (2) E T (3) T T*F (4) T F (5) F (E) (6) F id

E ´EE E+TE TT T*FT FF (E)F id

Page 111: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

112

E ´EE E+TE TT T*FT FF (E)F id

E

E ´E E E+T

T

E T T T*F

(

F ( E) E E+TE TT T*FT F F (E) F id

I0

I1

I2

I6

F T F I3

F idid I5

T I2

F I3

id I5

(

E E+ TT T*FT F F (E) F id

+

*T T* F F (E) F id

I7

F (E ) E E+T

EI8

T E E+ T T T*F

I9

F I3id

I5

(

FT T* F

I10id

I4

( I4

*I7

)

F (E)

+ I6

I11

Page 112: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

113

I1:E´E I2: E T I9: E E+T E E+T T T *F T T *F

I={X b , A , B }

若 {b}FOLLOW(A) FOLLOW(B)=则,面对当前读入符号 a ,状态 I 的解决方法: 1. 若 a=b, 则移进。 2. 若 a FOLLOW(A), 则用 A 进行归约。 3. 若 a FOLLOW(B), 则用 B 进行归约。 4. 此外,报错。这种解决方法是比较简单的,因此称作 SLR分析,由此构造的分析表,称作 SLR 分析表。

Page 113: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

114

对于表达式文法的例子, FOLLOW 集如下:

E’ $E $ ) +T $ ) + *F $ ) + *

五 . 非 SLR(1) 文法

例 4.19

(0) S´S (1) S L = R (2) S R

(3) L * R (4) L id (5) R L

I1:{ E´E EE+T}I2: {ET T T *F}I9:{E E+T T T *F}

Page 114: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

115

S´·S S · L = R S · R L · * R L · id R · L

I0 S

S´S ·I1

L S L · = R R L ·

I2

R S R · I3

*L * · R R · L L · * R L · id

I4

id

L id ·I5

=S L = · RR · L L · * R L · id

I6

RL * R · I7

L R L ·

*

idI8

R

S L = R · I9

LII8

* II4

idII5

FOLLOW(R)={=,$},非 SLR 文法。

Page 115: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

116

作业: 4.24

4.25

4.21

Page 116: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

117

4.7.3 LR(1) 分析表的构造

SL=RR L

I2

A Ik

aFOLLOW(A)

S Aw w

FOLLOW(A)比上面推导中 w 的第一个符号集要大,可能不存在下面的推导:

S Aax ax (w ax)*r m

r m

*r m

r m

r m

Page 117: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

118

栈中已识别出一个 L ,此时, a 是“ =” ,不能用“ RL” 进行归约,因为不存在“ R=” 的归范句型。 解决方法:使每个项目中含有更多的展望信息,使得能确切知道何时能进行归约。一 . LR(k) 项目 形式: [A , a1a2…ak]

,移进或待归约项目, a1a2…ak 不起作用。对归约项目 [A, a1a2…ak] ,仅当前输入符号串开始的前 k 个符号是 a1a2…ak

时,才能用 A 进行归约。 a1a2…ak 称向前搜索符号串。

Page 118: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

119

*r m

r m

二 . LR(1) 的有效项目集和转移函数

定义 4.10 (LR(1) 的有效项目 )

如果存在一个规范推导

    S δAw w

其中或w的第一个符号为 a,或w=ε而 a为$。一个 LR(1) 项目 [A→α·β,a] 对活前缀γ= 是有效的。 例 4 . 17 考虑文法 G:              S→CC             C→cC|d

*r m

r m

Page 119: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

120

因为有规范淮导

  S ccCcd cccCcd

故项目[ C→c·C,c] 对活前缀 ccc 是有效的。

若项目 [ A→α·Bβ,a] 对活前缀 γ=δα 是有效的,则存在一个规范推导 S δAax δαBβax

 假定 βax by ,则对每一个形如B→η 的产生式我们有规范推导

S Bby by

*r m

r m

*r m

r m

*r m

*r m

r m

Page 120: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

121

于是 , 项目[ B→·η,b] 对于活前缀 γ=δα 也是有效的。注意到 b 或者是从 β 推出的第一 个终结符号,或者 β ε而 b= a 。这两种可能性

结合在一起,则 b∈FIRST(βa)。 定义 4.12 设 I是 G 的一个 LR(1) 项目集, closure(I) 是从 I出发用下面三个规则构造的项目集 : 1 .每一个 I中的项目都属于 closure(I)。    2 .若项目[ A→α·Bβ, a]属于 closure(I) 且 B→η P, 则对任何 b∈FIRST(βa), 把 [ B→·η,b]加进 closure(I) 中。 3.重复执行 (2) 直到 closure(I) 不再增大为止。

*r m

Page 121: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

122

定义 4 . 13 设 I是 G 的一个 LR(1) 项目集, X 是一个文法符号,定义 go(I, X) = closure(J) , 其中   J={[A→ X·β,a] | 当[ A→ ·Xβ,a] I时 }     算法 4.10 计算 LR(1)的 closure(I)和go(I,x) FUNCTION closure(I:SET OF item):SET OFitem;     { REPEAT          FOR 任一 [A→ · Bβ,a] IN I            FOR 任一 B→η IN P              FOR 任一 b IN FIRST(βa)                I:=I∪{[B→ ·η,b]}       UNTIL I 不再增大 ;  Return (I) ; }    

Page 122: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

123

 FUNCTION go(I:SET OF item;x∈{VT∪VN}): VAR J:SET OF item ; SET OF item; {      J:={ };        FOR [A→α·Xβ,a] IN I          J:=J∪{[A→αX·β,a]} Return (closure(J)) ;}    算法 4.11 LR(1) 项目集规范族的构造方法:

  PROCEDURE items(G´);     {  C:={closure( {[ S’→·S, $ ]}) }; REPEAT  FOR 项目集 I∈ C 和 x {V∈ T V∪ N}           把 go(I,X) 加入到 C 中 UNTIL C 不在增大 ;}

Page 123: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

124

三 . 例示 LR(1) 项目集及规范族的构造

G(S´): (0) S´S (1) S CC

(2) C cC (3) C d

I0

[S´S, $]若 [A ,a] I

且 B P

则 [B,b] I

其中 b FIRST(a)

[S CC,$]

[C cC,c/d][C d,c/d]

Page 124: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

125

[S´S,$][SCC,$][CcC,c/d][Cd,c/d]

I0 S[S´S,$]

I1

C[SCC,$][CcC,$][Cd,$]

I2

c[CcC,c/d][C cC,c/d] [Cd,c/d]

I3d

[Cd,c/d]I4

C

[SCC,$] I5

c[CcC,$][CcC,$] [Cd,$]

I6

C[CcC ,$] I9

d

[Cd,$] I7

cd

cC

[CcC ,c/d] I8

d

Page 125: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

126

五 . 构造 LR(1) 分析表c d $ S C

0 S3 S4 1 21 acc2 S6 S7 53 S3 S4 84 r3 r3

5 r1

6 S6 S7 97 r3

8 r2 r2

9 r3

Page 126: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

127

作业: 4.30 4.31

Page 127: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

128

4.7.4 LALR 分析表的构造 LALR(lookahead-LR)技术。这种方法在实际中是经常使用的。 定义 4.14 如果两个 LR(1) 项目集去掉搜索符之后是相同的 ,则称这两个项目集具有相同的心 (core) 。 一个心就是一个 LR(0) 项目集。 定义 4.15 除去初态项目集外,一个项目集的核 (kernel) 是由此集中那些圆点不在最左端的项目组成。 LR(1) 初态项目集的核含有也仅含有 [S'→·S,$] 。

Page 128: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

129

[S´S,$][SCC,$][CcC,c/d][Cd,c/d]

I0 S[S´S,$]

I1

C[SCC,$][CcC,$][Cd,$]

I2

c[CcC,c/d][C cC,c/d] [Cd,c/d]

I3d

[Cd,c/d]I4

C

[SCC ,$] I5

c[CcC,$][CcC,$] [Cd,$]

I6

C[CcC ,$] I9

d

[Cd,$] I7

cd

cC

[CcC ,c/d] I8

d

Page 129: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

130

改进思路:

合并同心集可达到缩小构造分析表的状态数目;利用核代替项目集可以达到缩小项目集所占用的存储空间。介绍两种方法:

第一种方法:对于 G ,构造 LR(1) 项目集规范族 (DFA) ,然后,合并同心集,若合并后的同心集中没有移进归约冲突,则用其构造 LR 分析表 , 这种分析表称作 LALR 分析表。

图 4.27 中

I3 和 I6 I4和 I7 I8和 I9

Page 130: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

131

[S´S,$][SCC,$][CcC,c/d][Cd,c/d]

I0 S[S´S,$]

I1

C[SCC,$][CcC,$][Cd,$]

I2

c[CcC,c/d][C cC,c/d] [Cd,c/d]

I3d

[Cd,c/d]

I4

C

[SCC ,$]I5

c[CcC,$][CcC,$] [Cd,$]

I6

C[CcC ,$] I9

d[Cd,$] I7

cd

cC

[CcC ,c/d] I8

d

c[CcC,c/d/$][CcC,c/d/$][Cd,c/d/$] [Cd,c/d/$]

I47

[CcC ,c/d/$] I89

I36

I47

Page 131: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

132

讨论:1. 由于 go(I, X)仅仅依赖于 I的心,因此 LR(1) 项目集合并后的转换函数 go(I, X) 随 自身的 合并而得到。2. 动作 action 应当进行修改,使得能反映各被 合并集合的既定动作。3.   把同心的项目集合并为一,有可能导致冲突, 这种冲突不会是移进 - 归约冲突 ;但可能引起 归约 -归约冲突。 Ik:{[A,u1] [Ba,b] } au1= Ij:{[A,u2] [Ba,c] } au2= Ikj:[A,u1/u2] [Ba,b/c] a{u1, u2}=

Page 132: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

133

例:下面文法是 LR(1) 的,但不是 LALR(1)的。

S´ S S aAd bBd aBe bAe A c B c

[S´ S, $] [S aAd, $][S bBd, $][S aBe, $][S bAe, $]

[S´ S ,$]

[S a Ad, $][S a Be, $][A c, d][B c, e]

aS

[S b Bd, $][S b Ae, $][A c, e][B c, d]

b

A [S a A d, $]B [S a B e, $]c

[A c , d] [B c , e]

[A c , e][B c , d]

c

Page 133: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

134

LR(1)和 LALR(1) 分析上的差别

输入: ccd$

LR: 0 c 3 c 3 d 4 报错

LALR: 0 c 36 c 36 d 47

0 c 36 c 36 C 89

0 c 36 C 89

0 C 2 报错

Page 134: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

135

LALR 分析表的有效构造方法 用 LR(0) 的项目集的核项目代替项目集,为每个核项目配上搜索符号 ,得到 LALR 项目集族的每个项目集的核项目。 可行性 1. 用项目集的核构造 go函数 若 [BX, b ] Ikernel X{XTXN}

则 go(I,X) kernel : = [BX, b ]

若 [BC, b ] Ikernel

且 C A AXP

则 [AX, a] go(I,X) aFIRST(b)

*rm

Page 135: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

136

C A 存在规则链 :

C C11 C1 C22 Cn A n

2. 用项目集的核构造 action 表 (a) action[I, a] :=rA A Ikernel

若 [BC, b ] Ikernel

且 C A aFIRST(b)

则 action[I, a] :=r A

存在规则链 : C C11

C1 C22 Cn A n A

*rm

*rm

Page 136: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

137

(b) 若 [BC, b ] Ikernel

且 C a (最后一步不使用 - 产生式)

则 action[I, a] := S go(I,a)

C a P 或

C C11 C1 C22 Cn a

例示构造方法 G[S´]: (0) S´S (1) S L=R

(2) S R (3) L *R (4.23)

(4) L id (5) R L

*rm

Page 137: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

138

G[S´]: (0) S´S (1) S L=R (2) S R (3) L *R (4) L id (5) R L 1. 构造 G[S´]的 LR(0) 项目集的核

S ´S

I0S

S ´S I1

SL=RR L L

I2S R

R

I3

L * R*

I4

Lid

id

I5

= S L= R I6

R L * R I7

RL L I8*

id

RS L= R I9

L* id

I5

Page 138: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

139

2. 根据 I 的核和 X, 确定 go(I,X) 的核项目的搜索 符号。它分成自生的还是传播的。 看 用项目集的核构造 go函数的过程: 若 [BX, b ] Ikernel X{XTXN}

则 go(I,X) kernel : = [BX, b ]

项目 [BX, b ] 把 b传播给 [BX, b ]。 若 [BC, b ] Ikernel

且 C A AXP

则 [AX, a] go(I,X) aFIRST(b) 若,则 [AX, a] 中的 a 是自生的; 若 = ,则 [AX, a] 中的 a= b 是传播的。

*rm

Page 139: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

140

项目 [S´S, $] 中的 $ 是自生的。

算法 4.11 确定搜索符的方法 (# 是测试符号) FOR BK DO { J ´:=closure({[B, #]}); IF [AX, a] J ´且 a# THEN go(I, X)K 中 [AX, a] 的 搜索符 a 是自生的。 IF [AX, #] J ´ THEN 搜索符从 I 中的 B传播到 go(I, X)K中 AX }

Page 140: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

141

例示算法 4.11 G[S´]: S´S SL=R

SR L*R Lid R L

[S´S , #]

[SL=R , #]

[SR , #]

[L*R , =/#]

[Lid , =/#]

[R L , #]

S[S´S , #]

[SL =R , #][R L , #]

L

R[SR , #]

* [L*R , =/#]

id[Lid , =/#]

Page 141: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

142

S ´S

I0S

S ´S I1

SL=RR L L

I2S R

R

I3

L * R*

I4

Lid

id

I5

= S L= R I6

R L * R I7

RL L I8*

id

RS L= R I9

L* id

I5

3-4 根据( 2 )得到的信息,开始,给每个核项目加上自生的搜索符;然后,反复传播搜索符,直到每个核项目的搜索符不再增大为止。

$

=

=

$

$$

$

$

$

=

=

$

$

$

$

Page 142: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

143

4.8 LR 分析方法对二义文法的应用

 E→E+ E|E*E|(E)|id (4. 25)

E´EE E+EE E*E E (E)E id

E´EE E +EE E *E

E

E ( E)E E+EE E*E E (E)E id

(

E id id

id

(

+ E E+ EE E+EE E*E E (E)E id

E

E E+ E E E +EE E *E

Page 143: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

144

LR(k)和 LL(k) 的比较

1. A1 2

LL(k) 根据 FIRST(i) 确定使用哪条产生式;而 LR(k) 是在识别出整个后 ,再往后看 k 个符号 ,然后确定使用哪条产生式。

w

A

w

A

Page 144: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

145

LL(k) 文法都是 LR(k) 文法。2. 都能用形式化方法实现;3. 非 LR 结构 例: L={wwR w{a,b}*}

G[S]: SaSa bSb 4. LR(k) 分析用手工构造是不可能的。类 Pascal 语言的 LR(1) 分析表,估计要数千 个状态;由于有软件工具, LR 分析受到 广范重视。

Page 145: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

146

4.9 分析器的生成器 Yacc

一 . 用生成器 Yacc 构造翻译器的过程

Yacc编译器

yacc 源程序translate.y

y.tab.c

C编译器y.tab.c a.out

a.out源程序 输出

Page 146: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

147

二 . Yacc 源程序有三部分组成 声明 %% 翻译规则 %% C写的支持例程

三 . 例 4.21 台式计算器 G[E]: EE+T T T T*F F F (E) digit 读入一个整表达式,计算它的值并输出。

Page 147: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

148

%{# include <ctype.h>%}% token digit

%%line : expr´\n´ {printf(“%d\n”,$1);} ;expr : expr ´+´term {$$=$1+$3;} : term ;

Page 148: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

149

term : term ´*´facter {$$=$1*$3;} : facter ;facter : ´(´expr ´)´ {$$=$2;} : digit ;%%yylex(){ int c; c=getchar(); if (isdigit( c ){ yylval=c- ´0´; return digit ; } return c ; }

Page 149: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

150

声明部分 有任选的两节。 第一节处于分界符%{和%}之间,它是一 些普通的 C的声明; 第二节是文法记号的声明。

翻译规则部分 每条翻译规则由一个文法产生式和有关的语义动作组成。

支持例程部分 一些 C写的支持例程。例:词法分析器,错误恢复例程等。

   

Page 150: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

151

总结:

自顶向下分析

递归预测分析(递归子程序法)

非递归预测分析—— LL(1)

注意:首先消除左递归和提取左公因子。

自底向上分析

算符优先分析

LR 分析 : SLR(1), LR(1), LALR(1)

Page 151: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

152

LL(0) LR(0)

SLR

LALR

LR(1)LR(k)

LL(1)

LL(k)

Unambiguous Grammars Ambiguous Grammars

文法类的谱系

Page 152: 语法分析器是根据源语言的文法构造出来的。 用于程序语言的编译程序构造中的一些典型的语法分析方法及分析器的生成器 Yacc。

153

作业: 4.30

4.31 试构造 LALR( 1 )分析表。

4.21(e)