编译原理 compiler principles 第四章 语法分析 -...

Post on 10-Oct-2020

27 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

湖南大学信息科学与工程学院

软件工程系 杨金民

2018

编译原理Compiler Principles

第四章 语法分析

纵观全局

• 语言的规约1:词法单元的模式(串接结构,一维线性结构);

词法单元的模式 → 正则表达式 → DFA → 词法分析程序;

• 语言的规约2:上下文无关类的语法(嵌套结构:树形结构);

语法规则 → 上下文无关文法 → 推导 → 语法分析程序;

• 语言的规约3:上下文有关的语法:例如:1)变量先定义,后

使用规则,就近归属规则;2)函数调用中的参数个数,顺序,

类型的匹配规则;3)类型一致性规则;语义分析;

嵌套性 → 树形结构

include “my_head.h” int g_num; int main( int argc, char *argv ) { char szBuf [128]; char *psz = "Hello"; int p = 2;

g_num = 189; MyFun( p ); if (p > 0) { int p; p = strcpy(szBuf, psz); } return 0;}

root 块

函数块1main

函数块2MyFun

函数块3strcpy

if块1 while块2

if块1 if块2 while块3

控制语句→ 树形结构

if (i >j *k) then i+=10;else if (i >2) then i+=5;

stmt1

thenif elseexpr1 stmt2

语法分析树:

stmt3

thenif expr2 stmt4

表达式的计算过程 → 树形结构

position = initial + rate * 60

=+[id,1]

*[id,2][60][id,3]

语法分析树:

[id,1] [=,] [id,2] [+,] [id,3] [*,] [60]

本章要解决的问题

if (i >j *k) then i+=10;else if (i >2) then p = i+r*60;

=+id1

*id260id3

语法分析树:

验证词法单元的实例流(一维线性结构,串式结

构)符合语法规约,得出 树形结构的语法分析树

树状结构有什么好处

if (i >j *k) then i+=10;else if (i >2) then p = i + r * 60;

=+id1

*id260id3

语法分析树直白了处理过程;

语法分析树直白了逻辑关系;

stmt

thenif elseexpr stmt stmt

thenif expr stmt

第四章 语法分析

• 1. 上下文无关文法;

• 2. 推导;

• 3. 自顶向下语法分析– 递归下降分析– LL(1)分析

• 4.自底向上语法分析– LR(0)分析– SLR(1)分析– LR(1)分析– LALR(1)分析

语言中的词法单元

词法

预定义符 自定义符

保留字 运算符 标点符 特义符 变量 常量

数据变量 函数变量if while int

预定义符:相互独立,没有相交性。

; “ ...

id

语法主要通过预定义符来体现和标识。

自定义符的标识

符号表:

row_id name type class v_class row_num

1 return 预定义 保留字 120

2 ( 预定义 标点符号 120

3 initial 自定义 局部变量 int 120

4 <= 预定义 运算符 float 120

5 10 自定义 常量 数值 120

6 ? 预定义 标点符号 120

id,3

id,5

词法分析与语法分析对比

• 词法分析:如何从字符流切分识别为词法单元流。

return ( initial <= 10 ) ? 100 : ( position + initial ** 2 ) ;

forward

lexemeBegin匹配语言的词法单元的正则表达式

return ( initial 10<= ) ? position...

forward

position匹配语言的语法的文法,生成语法分析树

• 词法分析:如何从词法单元流生成语法分析树。

上下文无关的文法

词法 语法 语言

定义(类概念) 正则表达式 文法

实例词素 句子

所有实例的集合叫语言

运算|,联接,*

推导:从根往下的细化过程

归约:从叶往上的归纳过程

上下文无关文法,与之匹配的输入串对应的语法树

E→ E + T | T

T→ T * F | F

F→ (E) | id

观察与分析:这个文法 反映出了树型结构; 反映出了运算的优先级;

输入串: id + idE

id

+E T

FT

F

id

运算表达式的文法:

上下文无关文法的实例

E→ E + T | T

T→ T * F | F

F→ (E) | id

观察与分析:这个文法 反映出了树型结构; 反映出了运算的优先级;

输入串: id * id;

T

id

*T F

F

id

E

文法的实例(组合)

E

id

+E T

id + id *id

*T F

F

id

T

F

id

id + idE

id

+E TFT

F

id

id * id

T

id

*T F

F

id

E

E→ E + T | T

T→ T * F | F

F→ (E) | id

文法的实例(组合)

E→ E + T | T

T→ T * F | F

F→ (E) | id

id + idE

id

+E TFT

F

id

(id + id)

id

+

E

E TFT

F

id

F( )

TE

文法的实例(组合)

(id + id) *id (id + id)

T

id

*T F

F

id

E

id

+

E

E TFT

F

id

F( )

TE

id * id

T

id

*T F

E

id

+

E

E TFT

F

id

( )F

上下文无关文法

E→ E + T | T

T→ T * F | F

F→ (E) | id

观察与分析:这个文法 反映出了树型结构; 反映出了运算的优先级; 反映出了运算的交换律; 具有开放性、灵活性,适应性; 具有高度抽象性; 具有全覆盖性;

E

id

+E T

id + id * id

*T F

F

id

T

F

id

对上下文无关文法的特性认识

E→ E + T | T

T→ T * F | F

F→ (E) | id

id + id * idE

id

+E T*T F

F

id

T

Fid

开始符号;树根

非终结符号:展开关系;

产生式: 从左到右:推导,分析;

从右到左:规约,归纳;

的 语法分析树

对输入串的推导

表达式输入串

什么叫语法分析

E→ E + T | T

T→ T * F | F

F→ (E) | id

语法分析器

对词法分析后的源程序:输入串(符号串);

验证输入串符合文法吗?

不符号的话,就说明源程序有语法错误;

符合的话,推导出输入串的语法分析树。输入串:id + idE

id

+E TFT

Fid

如何推导?

文法:

理想状态:对符号串从左到右逐个扫描一趟,

就能完成。

文法中平行关系的扩展

E→ E + T | E - T | T

T→ T * F | T F | F

F→ (E) | id

运算优先级最低

运算优先级次低

高级

运算优先级最低的,在最后处理,自然应该在树根。

文法本身自然应该是树型结构;

上下文无关文法

开始符号; 树根

终结符号,非终结符号; 层次结构

产生式: 头部(一个符号),产生式体:符号串|终结符号|ε;

非终结符号表达了语言的层次结构;

• E→ E + T | E - T | T

• T→ T * F | T / F | F

• F→ (E) | id

① E→ E + T ② E→E - T ③ E→ T④ T→ T * F ⑤ T→ T / F ⑥ T→ F⑦ F→ (E) ⑧ F→ id

文法的实例(组合)

输入串:(id + id)E

E

id

+E TFT

F

id

F( )

T

(id + id) *id

T

id

*T F

E

id

+

E

E TFT

F

id

( )F

id + id+id

id

F+ TE

TF

id

+E TE

idF

2. 基于文法的推导

推导:从根往下,任用产生式,进行细化的过程,直至树的叶

结点不含非终结符号,仅含终结符号。

一步推导:任用一个产生式,对树中的某个(非终结符)叶结

点深化一层。

E E

+E T

*T F

E

+E T

自顶向下的语法分析之一:基于文法对输入串的最右推导

① E→ E + T ② E→ T③ T→ T * F ④ T→ F⑤ F→ (E) ⑥ F→ id

输入串:

E E

+E T

*T F

E

+E T

*T F

E

+E T

id

文法:

id + id *id

自顶向下的语法分析之一:基于文法对输入串的最右推导

*T F

E

+E T

idF*T F

E

+E T

idFid

* F

E

+E T

idT

Fid

T

Fid

① E→ E + T ② E→ T③ T→ T * F ④ T→ F⑤ F→ (E) ⑥ F→ id

输入串:文法:id + id *id

自顶向下的语法分析之二:基于文法对输入串的最左推导

E E

+E T

E

+E T

E

+E T

TF

T

① E→ E + T ② E→ T③ T→ T * F ④ T→ F⑤ F→ (E) ⑥ F→ id

输入串:文法:id + id *id

自顶向下的语法分析之二:基于文法对输入串的最左推导

* F

E

+E T

idT

Fid

T

Fid

E

+E T

T

Fid

* F

E

+E T

T

Fid

T

① E→ E + T ② E→ T③ T→ T * F ④ T→ F⑤ F→ (E) ⑥ F→ id

输入串:文法:id + id *id

基于文法推导中的概念

推导后,树中的叶结点,从左至右,组成的符号串,叫句型

(sentential form)。

如果句型中不含非终结符号,自然只含终结符号,那么就叫句

子(sentence)

文法G,由它生成的语言L(G),自然就是所有句子的集合。

最左推导(leftmost derivation):对树中的叶结点,总是选择最左的非终结符号叶结点,来执行推导。

最右推导(rightmost derivation):对树中的叶结点,总是选择最右的非终结符号叶结点,来执行推导。

概念:句型,句子,最右句型

E E

+E T

E+T

E

+E T

T

Fid

id+T

* F

E

+E T

idT

Fid

T

Fid

id+id*idE

句型: 树的叶节点从左到右构成的串 句子: 只含终结符的句型

最左句型: 最左推导中出现的句型

最右句型: 最右推导中出现的句型

自顶向下的最右推导的特性

*T F

E

+E T

id* F

E

+E T

idT

Fid

T

Fid

*T F

E

+E T

idFid

*T F

E

+E T

idF

id+id*id

E+T*id

id+id*id

E+F*id

id+id*id

E+id*id

id+id*id

id+id*id

最右句型

推导是从输入串的最右端(即末尾)开始逐一来匹配:不切实际输入串

自顶向下的最左推导的特性

* F

E

+E T

idT

Fid

T

Fid

E

+E T

T

Fid

* F

E

+E T

T

Fid

T * F

E

+E T

T

Fid

TFid

id+id *id

id+T

id+id *id

id+T*F

id+id *id

id+id* F

id+id*id

id+id*id

最左句型

推导是从输入串的最左端(即起始)开始逐一来匹配:切合实际输入串

文法的二义性

• 如果某个句子:有多个最左推导,或者多个最右推导。

• 例如文法:E→E+E | E*E | (E) | id; 句子: id + id * id

id

+

E

E E

*E E

id id

E

*E

id

E

EE +

id id

不是想要的原因:将+和*等同看待,没有区分优先级

比较两个文法

文法1:E→E+E | E*E | (E) | id;

文法2: E→ E + T | T

T→ T * F | F

F→ (E) | id

*是+的子结点;

(E) | id 又是*的子结点,

体现了优先级;E

+E T

TE +

+E T

TE +

+和*是平行关系

类似于正则表达式的*运算

句型:E+T+T+T+T = E(+T)*

E→ E + T涵盖了*运算

文法 VS 正则表达式

• 并运算在文法中也有;

• 连接运算 → 产生式;

• *运算 → 递归 :E→ E+T;

用正则表达式表达的,用文法

都能表达。超集与子集的关系。

b

start 0 1 2b

3a b

(a|b)*abb

a文法:

A0→aA0 |bA0 |abb

文法 比 正则表达式更加强大

• 或运算在文法中也有;

• 连接运算 → 产生式;

• *运算 → 递归 :E→ E+T;

用正则表达式表达的,用文法

都能表达。超集与子集的关系。

E→ E + T | TT→ T * F | F

F→ (E) | id

对于输入串:(((id + id) * id+idid)*id+id)*id

括号配对的语法,正则表达式无法表达。

E→ E + T | T T→ T * F | F F→ (E) | id

文法比正则表达式强大,但效率要低

E→ E + T | T

T→ T * F | F

F→ (E) | id

(id + id) * id;

E

+

E

T

*T

id

F

E

)(

T

T

id

F id

F

效率显然要比正则表达式的低。

语法树中出现了很多非终结符

4.3 文法设计

消除文法的左递归;

提取左公因子。

消除文法的二义性;

4.3节的知识,都是4.4 节 “自顶向下的语法分析”中遇到和

要解决的问题,因此,融合到4.4节中去,不单独讲。这样显

得更加实在,有针对性。

4.4节. 基于自顶向下和最左推导的语法分析

* F

E

+E T

idT

Fid

T

Fid

E

+E T

T

Fid

* F

E

+E T

T

Fid

T * F

E

+E T

T

Fid

TFid

id+id *id

id+T

id+id *id

id+T*F

id+id *id

id+id* F

id+id*id

id+id*id

最左句型

推导是从输入串的最左端(即起始)开始逐一来匹配:切合实际输入串

3、自顶向下、最左推导下的语法分析例子

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else smt | ε

stmt

thenif expr stmt E

已知文法G:

对于输入串:

自顶向下的语法分析例子(cont.)

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else smt | ε

stmt

thenif expr stmt

E1

E

已知文法G:

对于串

自顶向下的语法分析例子(cont.)

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else smt | ε

stmt

thenif expr stmt

E1

E

已知文法G:

对于串

自顶向下的语法分析例子(cont.)

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else smt | ε

stmt

thenif expr stmt

E1

E

已知文法G:

对于串

thenif expr stmt E

自顶向下的语法分析例子(cont.)

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else smt | ε

stmt

thenif expr stmt

E1

E

已知文法G:

对于串

thenif expr stmt EE2

自顶向下的语法分析例子(cont.)

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else smt | ε

stmt

thenif expr stmt

E1

E

已知文法G:

对于串

thenif expr stmt EE2

自顶向下的语法分析例子(cont.)

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else stmt | ε

stmt

thenif expr stmt

E1

E

已知文法G:

对于串

thenif expr stmt EE2 S1 stmtelse

自顶向下的语法分析例子(cont.)

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else smt | ε

stmt

thenif expr stmt

E1

E

已知文法G:

对于串

thenif expr stmt EE2 S1 stmt

S2

else

自顶向下的语法分析例子(cont.)

if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else smt | ε

stmt

thenif expr stmt

E1

E

已知文法G:

对于串

thenif expr stmt EE2 S1 stmt

S2

ε所有叶结点都为终结符!

基于自顶向下和最左推导的语法分析,遇到的问题之一:左递归

对带左递归的文法:

E→ E + id | id

输入串:

E

+E id4

E

+E id4

+E id3

E

+E id4

+E id3

+E id2

自顶向下的最左推导:

最左句型:E+id4 E+id3+id4 E+ id2 + id3 + id4

推导是从输入串的最右端开始逆向逐一来匹配:不切合实际

id1 + id2 + id3 + id4

基于自顶向下和最左推导的语法分析,遇到的问题之一:左递归

对带左递归的文法:

E→ E + id | id id1 + id2 + id3 + id4

输入串:

最左句型:

id1+ id2 + id3 + id4

推导是从输入串的最右端开始逆向逐一来匹配:不切合实际

E

+E id4

+E id3

+E id2

id1

解决办法:消除左递归

带左递归的文法:E→ id E'E'→+ id E' |

消除左递归后的文法:

E→ E + id | id

E

+E id4

+E id3

+E id2

id1

E

id1 E'

id2+ E'

id3+ E'

id4+ E'

id1 + id2 + id3 + id4输入串:

解决办法:消除左递归

带左递归的文法:E→ id E'E'→+ id E' |

消除左递归后的文法:

E

id1 E'

E

id1 E'

id2+ E'

自顶向下的最左推导:

最左句型:id1E' id1+id2E' id1 + id2 + id3E'

E→ E + id | id

E

id1 E'

id2+ E'

id3+ E'

变成了从输入串的最左端开始顺向逐一来匹配:切合实际

解决办法:消除左递归

带左递归的文法:E→ id E'E'→+ id E' |

消除左递归后的文法:

最左句型:

E→ E + id | id

E

id1 E'

id2+ E'

id3+ E'

id4+ E'

E

id1 E'

id2+ E'

id3+ E'

id1 + id2 + id3E'

消除左递归的特性

带左递归的文法:E→ id E'E'→+ id E' |

消除左递归后的文法:

当某个非终结符的产生式含左递归时:

将该非终结符的所有产生式放到一起;

其产生式分两类:含左递归的, 不含左递归的;

E→ E + id | id

好处:实现了对输入串从左至右逐一匹配;

代价:多引入了一个非终结符,语法树变得复杂,效率低

解决办法:消除左递归

带左递归的文法:

E→ E + id | id

E

+E id4

+E id3

+E id2

id1

id1 + id2 + id3 + id4输入串:

解决办法:消除左递归

带左递归的文法:E→ id E'E'→+ id E' |

消除左递归后的文法:

E→ E + id | id

E

id1 E'

id2+ E'

id3+ E'

id4+ E'

id1 + id2 + id3 + id4输入串:

消除左递归例子1:严格按照公式处理

E→ E + T | T

T→ T * F | F

F→ (E) | id

E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

文法:E→ TE'E' → +TE' | ε

T→ FT'T'→ * F T' | ε

消除间接左递归例子2(书上例4.20)

文法: S → Aa | b;

A→Ac | Sd | ε

解:把A代入S得:

S → Aca |Sda | a| b; 这里还含A,再要进一步消A,死循环,不行;

直接左递归;

间接左递归;

消除间接左递归例子2(书上例4.20)

文法: S → Aa | b;

A→Ac | Sd | ε

解:反过来,把S代入A得:

A→Ac | Aad | bd | ε ,只有A了,可行;

严格按照公式消除左递归:

A→bdA' | εA';

A'→cA' | adA' | ;

即:A→bdA' | A';

A'→cA' | adA' | ;

基于自顶向下和最左推导的语法分析,遇到的问题之二:左公因子

stmt→ if expr then stmt | if expr then stmt else stmt | other

stmt

thenif elseexpr stmt stmt

thenif expr stmtS1E2

S2E1

thenif expr stmt

thenif expr stmtS1E2 S2

E1

stmt

else stmt

对于串 if E1 then if E2 then S1 else S2

看到if时,是选第一个产生式,还是第二个?不好决策!不同选择,导致不同结果

当含左公因子时的解决办法:提取左公因子

stmt→ if expr then stmt | if expr then stmt else stmt | other

对于输入串 if E1 then if E2 then S1 else S2

stmt → if expr then stmt E | otherE → else stmt | ε

stmt

thenif expr stmt

thenif expr stmtS1E2 stmt

S2

E1E

else

E

ε

对提取左公因子的认识——要彻底

stmt→ if expr then stmt | if expr then stmt else stmt | other

是指某个非终结符,它有多个产生式,其产生式体的左端有公因子;

导致在推导时,有多个产生式匹配,不知道选哪个才对。

解决办法:把非公因式部分单独拿出来,新引入一个非终结符,来

表达它,注意 。

策略:把差异部分,延后再来确定;

代价:新引入了一个非终结符;

stmt → if expr then stmt E | otherE → else stmt | ε

提取左公因子的一般化表述

如果非终结符A的产生式:A→ β1 | β2 | β3 |

那么就为左公因子,将该产生式变换为:

A→ A’ |

A'→β1 | β2 | β3

其中,β1 , β2 ,β3 ,没有左公因子。

的左端不含

提取左公因子的例子

S → +id | -id | T+(id)| T-(id) | -(id) | T-idT → (id) × T | (id)

S → M id | TME | −E | T-idT → E × T | EE → ( id )M → + | −

文法:

观察:文法中的非终结符E和M都是指终结符,因此,可将其置换

S → +id S → -id S → T+(id)S → T-(id) S → -(id)S → T-id

T → (id) × TT →(id)

T是以“(”开头

提取左公因子的例子(cont.)

S → +id S → -id S → T+(id)S → T-(id) S → -(id)S → T-id

T → (id) × T T →(id)

S → +id S → -P S → TQ

P → id | (id)Q →+(id) | -(id) | -id

T → (id) R

R → × T |

Q →+(id) Q →-N

N →(id) | id

随堂测试1

E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

id + id * id

已知文法G:

对于输入串自顶向下的语法分析,写出每一步推导。

自顶向下、最左推导的语法分析中遇到的问题之三:有多个产生式可选,到底选哪个?

E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

id + id * id E

id

+T E'

*

T

F

F T'

id ε F T'

id

E'ε

T'ε

已知文法G: 对于串 其语法分析树:

推导时,对一个非终结符,当有多个产生式供选择时,到底选

择哪一个产生式呢?

观察与分析:寻求解决问题的突破口

选择F→ (E) ,还是F→id呢?E

T E'

F T'?

当前输入符与F→id匹配,不与F→ (E)

匹配,自然只能选F→id。

id + id * id

输入串:E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

文法:

观察与分析:寻求解决问题的突破口

E

T E'F T'id ?

是选择T'→ * F T' ,还是T'→ ε呢?

选T'→ * F T' 不行,因为当前输入符'+'与该产生式的第一个符号'*'不匹配。选T'→ε行吗?

id + id * id

输入串:E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

文法:

观察与分析:寻求解决问题的突破口

E

T E'F T'id ?

不能选T'→ * F T',那么选T'→ε行吗?

可行的条件是紧随T'之后的E’在推导后,其第一个终结符必须

是'+', 来与当前输入符'+'匹配。

id + id * id

输入串:E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

文法:

引出了求FOLLOW(T')的idea.

观察与分析:寻求解决问题的突破口

id + id * id

输入串:E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

文法:

选择E' → +TE' 还是E' → ε呢?

E

T E'F T'id ε

对接下来的推导步的观察

E

+T E'

TF T'

id ε F T'E'

选择F→ (E) 还是F→id呢?

id + id * id

输入串:E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

文法:

E

+T E'

TF T'

id ε F T'E'

选择T'→ * F T' 还是T'→ ε呢?

?id

对接下来的推导步的观察

id + id * id

输入串:E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

文法:

E

+T E'

*

T

F

F T'

id ε F T'

id

E'ε

T'ε?

选择F→ (E) 还是F→id呢?

对接下来的推导步的观察

id + id * id

输入串:E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

文法:

对刚才观察进行归纳与提升:形式化处理

选择A的哪一个产生式?是问题的关键。

对于A→和 A→β, FIRST()的含义是从推导得到的串的第一

个终结符的集合

对于A→和 A→β, 如果FIRST() FIRST(β) = , 那么只要查

看下当前输入符a,是在FIRST()中, 还是 在FIRST(β)中,就

知道要选A→ 还是A→ β。

对于A→和 A→, FOLLOW(A)是指紧跟在A后面的终结符号的

集合。如果当前输入符a在FOLLOW(A)中,又不在 FIRST()中,

就可选A→

FIRST()和FOLLOW( )的求法

开始符号S

终结符号

第1层非终结符号

第2层非终结符号

第n层非终结符号...FIRST() FOLLOW( )

语法分析树推导完成,输入串的末尾符也已匹配完毕,当前输入

符自然为'$', 因此FOLLOW(S) += {$};

初始时:所有产生式:FIRST()=, 所有非终结符:FOLLOW( )=;

FIRST()求法:对非终结符号,由低到高排序,逐一扫描其产生式, 计算每一产生式的FIRST()

算法:对于A→X1X2...Xn。

ε_stand = true;i = 1;while (ε_stand AND i ≦ n) { FIRST() += FIRST(Xi) - ε if εFIRST(Xi) then ε_stand = false; else i++;}if (ε_stand AND i == n+1) then FIRST() += ε ;

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

E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

当一个非终结符号C有多个产生式时:A→1,...., A→n,FIRST(C)=FIRST(1) ...FIRST(n)

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

FIRST(F)={ id, ( }

FIRST(T')={ *, }

FIRST(E')={ +, }

FOLLOW()求法

对于产生式:A→BC,

FOLLOW(C) += FOLLOW(A);

FOLLOW(B) += FIRST(C) - ,

如果ε FIRST(C), 那么进一步有FOLLOW(B)+=FOLLOW(A);

算法:

对非终结符,由高到低依次排序;

然后逐一计算每个非终结符的FOLLOW()。设当前非终结符

为C,对右部含C的所有产生式,分别计算FOLLOW(C).

FOLLOW()求法举例(cont.) 文法: FIRST(E) ={(,id}

FIRST(E') = {+, ε};FIRST(T) = {(,id}FIRST(T')={*, ε}FIRST(F) = {(, id}

非终结符由高到低排序为: E, E', T, T', F:

1)计算FOLLOW(E), 右部含E的产生式有:F→ (E)由F→ (E) FOLLOW(E) += { ) } = { ), $ };

2)计算FOLLOW(E'), 右部含E'的产生式有:E→ TE',E' → +TE':由E→ TE' FOLLOW(E') += FOLLOW(E)={ ), $ }由E' → +TE' FOLLOW(E') += FOLLOW(E'),没意义;

E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

FOLLOW()求法举例(cont.)

3)计算FOLLOW(T), 右部含T的产生式有:E→ TE',E' → +TE':

由E→ TE' FOLLOW(T) += FIRST(E') - = {+}因FIRST(E') FOLLOW(T) += FOLLOW(E) = { +, ), $}

由E' → +TE' FOLLOW(E') += FIRST(E') - ,在上面已处理

因FIRST(E') FOLLOW(T) += FOLLOW(E') = { +, ), $ }

文法: FIRST(E) ={(,id}FIRST(E') = {+, ε};FIRST(T) = {(,id}FIRST(T')={*, ε}FIRST(F) = {(, id}

E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

FOLLOW()求法举例(cont.)

4)计算FOLLOW(T'), 右部含T'的产生式有T→ FT' , T'→ * F T',

由T→ FT' FOLLOW(T') += FOLLOW(T) = {+, ), $}

由T'→ * F T' FOLLOW(T') += FOLLOW(T'),没意义

文法: FIRST(E) ={(,id}FIRST(E') = {+, ε};FIRST(T) = {(,id}FIRST(T')={*, ε}FIRST(F) = {(, id}

E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

FOLLOW()求法举例(cont.)

5)计算FOLLOW(F), 右部含F的产生式有T→ FT' , T'→ * F T':

由T→ FT' FOLLOW(F) += FIRST(T') - = { * }

因FIRST(T') FOLLOW(F) += FOLLOW(T) = {*,+ , ), $}由T'→ * F T' FOLLOW(F)+= FIRST(T')-,在上面已处理

因FIRST(T') FOLLOW(F) += FOLLOW(T') = {*, +, ), $}

文法: FIRST(E) ={(, id}FIRST(E') = {+, ε};FIRST(T) = {(,id}FIRST(T')={*, ε}FIRST(F) = {(, id}

E→ TE'E' → +TE' | εT→ FT'T'→ * F T' | εF→ (E) | id

FIRST()和FOLLOW( )在推导决策中的作用

对于产生式:A→和A→

id + id * id

当前符a

输入串:

FIRST() FIRST(β) = aFIRST(): 选A→aFIRST(β): 选A→β

FIRST() FOLLOW(A )= aFIRST(): 选 A→aFOLLOW(A): 选A

注意:FIRST():由A的产生式A→决定;

FOLLOW(A): 与A的产生式无关,只与右部中含A的产生式有关

对于产生式:A→和A→β

推导决策的推广延伸

对于非终结符A的所有产生式:A→1,A→2,...., A→n

仅只存在两种情形:

1,1,...,n都不为;

1,1,...,n中仅只一个为;设n=

FIRST(1) FIRST(2) ... FIRST(n) =

aFIRST(i): 选A→i , 1≦i≦n

FIRST(1) FIRST(2) ... FIRST(n-1) FOLLOW(A )=

aFIRST(i): 选 A→ i, , 1≦i≦n-1

aFOLLOW(A): 选A

FIRST()和FOLLOW( )的用途—— 换一种说法

对于产生式:A→ | β p = id + id * id;

当前输入符a

输入串

aFIRST() OR (εFIRST() and aFOLLOW(A))

自顶向下的语法分析中,设当前输入符为a,要推导的非终结符A:

产生式A→能被选用的条件为:

文法G不是LL(1)文法的条件

对于文法G,有产生式A→ | β,如果FIRST() FIRST(β) ,

就无法决策选A→ 还是A→β,那么G就不是LL(1)文法。

对于文法G,有A→和A→,如果 FIRST() FOLLOW(A )

就无法决策选A→ 还是A→,那么G就不是LL(1)文法。

LL(1)预测分析表

非终结符输入符

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→(E)

E→ TE'E' → +TE' E' →εT→ FT'T'→ * F T'T' → εF→ (E) F→ id

A→能被选用条件:aFIRST() OR (εFIRST() and aFOLLOW(A))

FOLLOW(E) = {),$}FOLLOW(E') = {),$}FOLLOW(T) = { +, ), $}FOLLOW(T') = {+,), $} FOLLOW(F) = {*, +, ),$}

FIRST(E) ={(, id}FIRST(E') = {+, ε};FIRST(T) = {(,id}FIRST(T')={*, ε}FIRST(F) = {(, id}

判断文法G是不是LL(1)文法的判别

非终结符输入符

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→(E)

LL(1)预测分析表中的某个格子(cell)中,如果有两个产生式,

那么就存在产生式选取冲突问题,说明这个文法就不是LL(1)

文法。

对FIRST( )和FOLLOW( )的认识

FIRST(), FOLLOW()都是刻画文法特征的一种人为定义,其值

与输入串无关。仅只有在对输入串进行语法分析时,才与当前输

入符关联起来。

FIRST(A):由A的产生式(即左部为A)决定;

A可能有多个产生式:A→1,A→2,...., A→n

然后求:FIRST(A)= FIRST(1)FIRST(2) ...FIRST(n)

对于A,有用的是:FIRST(1), FIRST(2), ..., FIRST(n)

FIRST(A)的作用仅仅是为了随后求FIRST(X),FOLLOW( )

要用到.

FIRST(A)指所有可能的情况下,得到的结果的集合。

对FIRST()和FOLLOW()的认识(cont.)

FOLLOW(A):向前看符号,针对文法,而不是针对输入串。

注意:指所有可能的情况下,得到的结果的集合。

FOLLOW(A): 与A的产生式无关,只与左部中含A的产生式有

关。

对LL(1)预测分析表的认知

非终结符输入符

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→(E)

预测分析表是从文法得出的,是元信息。

表中的某个格子中,如果有不止一个产生式,那么

该文法就不是LL(1)文法。

预测表解决了产生式的选择问题。

自顶向下的语法分析

语言的文法中,每个非终结符号:有一个过程(函数)。首先调用开始符号A的过程:void A() { 选择A的某个产生式:A→X1X2...Xk; for i = 1 to k) { if (Xi是一个非终结符) 调用Xi( ); if (Xi == 当前的输入符号a) 读入下一个输入符号; else 发生了一个错误,错误处理; }}

E

id

+T E'

*

T

F

F T'

id ε F T'

id

E'ε

T'ε

基于栈的自顶向下语法分析

E

id

+T E'

*

T

F

F T'

id ε F T'

id

E'ε

T'ε

E E' T

E' T' F

E' T'

E' E' T +

E' T' F

E' T' F

E' T'

E' T' F *

预测分析中的错误恢复

• 错误恢复

– 当预测分析器报错时,表示输入的串不是句子。

– 使用者希望预测分析器能够进行恢复处理后继续语法分析过

程,以便在一次分析中找到更多的语法错误。

– 恢复可能不成功,之后找到的语法错误可能是假的。

– 错误恢复时可用的信息:栈里面的符号,待分析的符号

• 两类错误恢复方法:

– 应急模式(恐慌模式)

– 短语层次的恢复 该部分,学生自己看书,自学!

总结:自顶向下、最左推导的语法分析LL(1)

对文法作消除左递归,提取左公因子处理,

对每个产生式,计算FIRST();

对每个非终结符号,计算FOLLOW();

填写预测分析表;

判断其是否是LL(1)文法;

对输入串推导出语法分析树;

一定要彻底!

自顶向下最左推导中的问题:对于一个非终结符号,当存

在多个产生式时,推导中选用哪个产生式?

总结:自顶向下、最左推导的语法分析LL(1)

对文法做消除左递归处理,做提取左公因子处理,都只是形式

上的处理,不会改变文法的语义。

消除左递归和提取左公因子,都会新引入非终结符,增加产生

式,使得语法分析树变复杂,于是语法分析效率降低。

当文法G的产生式中,存在直接或者间接左递归,或者某个非终

结符的产生式存在左公因子时,那么该文法就不是LL(1)文法;

文法G因存在左递归或者左公因子而不是LL(1)文法。对文法G做消

左递归和提左公因子处理后,得到文法G', 可能是LL(1)文法;

4、自底向上、从左到右的语法分析LR

前面讲完了三个topic:

上下文无关文法;

基于推导的语法分析;

自顶向下、最左推导的语法分析(书上4.4节);

接下来将第四个topic:

自底向上、从左到右的语法分析LR,从书上4.5节开始

动机:对输入串id + id *id执行最右推导进行观察

*T F

E+E T

idFid

E+T E+T*F E+T*id E+F*id

* F

E+E T

idTF

id

T

E

*T F+E T

id*T F

E+E T

idF*T F

+E TE

+E TE

* F

E+E T

idTF

id

TF

id

* F

E+E T

idTF

id

TF

E+id*id T+id*id F+id*id id+id*id

自顶向下:最左推导与最右推导对比

E

+T E'

*

T

F

F T'

id ε F T'

id

E'ε

T'ε

id + id * id输入串:

id

* F

E

+E T

idT

Fid

T

Fid

最左推导 最右推导

树不简洁,繁杂;

切合实际:对输入串,基于从左到右逐一匹配,来推导

树简洁;

不切合实际:基于从右到左逐一匹配,来推导

question: 两者的优点能不能两者兼得?

自顶向下:最左推导对比最右推导

E

+T E'

*

T

F

F T'

id ε F T'

id

E'ε

T'ε

id + id * id输入串:

id

* F

E

+E T

idT

Fid

T

Fid

最左推导 最右推导

树不简洁,繁杂

原因:消除左递归和提取左公因子,都

引入了新的非终结符号,新的产生式

树相对简洁;

4.5 自底向上的语法分析

• 执行最右推导

E→ E + T | TT→ T * F | FF→ (E) | id

id + id *id输入串:文法:

推导:对产生式,用右边代替左边:细化。最右推导。对输入串(id + id *id)

*T F

E+E T

idFid

E+T E+T*F E+T*id E+F*id

* F

E+E T

idTF

id

T

E

*T F+E T

id*T F

E+E T

idF*T F

+E TE

+E TE

* F

E+E T

idTF

id

TF

id

* F

E+E T

idTF

id

TF

E+id*id T+id*id F+id*id id+id*id

反过程——自底向上,从左到右的语法分析归约:对产生式,用左边代替右边:归纳。

移入:将当前输入符移到栈中

id+id*id

* F

E+E T

idTF

id

TF

id idid

* F

E+E TTF

TF

栈:

输入串:

id

* F

E+E T

idTF

id

TF

id

+id*id

F

+id*id

归约:将产生式右边从栈中弹出,再将产生式左边非终结符号压到栈中:即左部替换右部

* F

E+E T

idTF

id

T

T

+id*id

E→ E + T | TT→ T * F | FF→ (E) | id

反过程——自底向上,从左到右的语法分析(2)

* F

E+E T

idTF

id

E

+id*id

* F

E+E T

idTF

id

E+

id*id

移入

* F

E+E T

idTF

E+F

*id

归约

* F

E+E T

idTF

id

E+id

*id

移入

E→ E + T | TT→ T * F | FF→ (E) | id

归约

反过程——自底向上,从左到右的语法分析(3)

* F

E+E T

idT

E+T

*id

归约

* F

E+E T

idT

E+T*

id

移入

* F

E+E T

idT

E+T*id

移入

反过程——自底向上,从左到右的语法分析(4)

E+E T

* FT

E+T*F

归约

E+E T

E+T

归约

E

归约

句柄总是出现在栈顶 最右句型

id* FT

E+E T

把栈与输入串连接起来观察E→ E + T | TT→ T * F | FF→ (E) | id

id + id *id输入串:文法:

idE+T*

E+T*id E+T*F E+T E

*idE+id *idE+F *idE+T

id+id*id移入

+id*idF +id*id +id*idT +id*idE id 规约 规约 规约

id*idE+ 移入 规约

规约 规约

规约 移入

移入 规约

句柄

概念

idE+T*

E+T*id E+T*F E+T E

*idE+id *idE+F *idE+T

id+id*id移入

+id*idF +id*id +id*idT +id*idE id 规约 规约 规约

id*idE+ 移入 规约

规约 规约

规约 移入

移入 规约

句子句型

最右句型格局:栈+剩余输入串

句柄

基于移入—规约的语法分析框架

E→ E + T | TT→ T * F | FF→ (E) | id

id + id *id $

移入

输入串:

文法:

当前符

$ 栈: 规约 接受

报错

操作:

? ?

移入—规约语法分析要解决的问题——冲突

规约/规约冲突

*idE+T

遇到格局:

移入/规约冲突

如何决断?

选移入,还是归约?

选哪个产生式进行归约?

idea:跟踪栈中栈顶句柄的满足程度。句柄没有形成,就移入

解题思路(技术路线):利用DFA

E→ E + T E→T

top related