编译原理复习,积累语言经验 for kiana

默认的规则

VNV_N是非终结符集合
VTV_T是终结符集合
终结符和非终结符的集合是互斥的
一般字母的大写是非终结符,小写是终结符
V=VNVTV = V_N \bigcup V_T
GG是文法/语言
PP是产生式,也就是推导的式子abca \rightarrow bc
SS是开始符号
abcbcaa\rightarrow bc 是推导 bc \rightarrow a 是规约
::=::=和\rightarrow或者其他的箭头符号是一样意思
字母后的*表示0个或多个
+字母后的+表示1个或多个

0型文法

G=(VN,VT,P,S)G =(V_N,V_T,P,S),如果GG的每个产生式αβ\alpha \rightarrow \beta 都满足 α,β(VNVT)\alpha ,\beta \in(V_N \bigcup V_T)^*, 并且α\alpha至少有一个非终结符!
这样的文法GG就是一个0型文法!

  • 0型文法的能力相当于Turing Machine
  • 任何0型文法语言都是递归可枚举的;属于递归可枚举集的文法,一定是0型语言
  • 0型文法对文法规则的表示形式不作任何限制,从而为定义的语言提供充分的描述功能
  • 但是,0型文法不保证语言的递归性,不能确定语句是否合法,所以很少用于定义自然语言!

1型文法(上下文有关文法)

上下文有关文法对应线性有界自动机,在0型文法的基础上,规定了
αβ  ,  have      αβ\forall \alpha \rightarrow \beta\;,\;have \;\;\;|\alpha| \leq |\beta|

  • 上下文有关意味着产生式左部只有一个非终结符!

2型文法(上下文无关文法)

上下文物管文法对应下推自动机——下推自动机比其他有限状态自动机复杂,除了有限状态组成部分外,还包括一个长度不受限制的栈!
2型文法在1型文法的基础上,规定了
αβ  ,  have      α    VN\forall\alpha \rightarrow \beta\;,\;have \;\;\; \alpha \;\in\;V_N

  • 上下文无关意味着产生式左部只有一个非终结符!
  • 2型文法已经广泛用于定义程序设计语言,这种文法规定了左部必须只能是单一的非终结符——非终结符通过文法规则的扩展性重写是相互独立的,不受其他符号的影响,所以称为上下文无关文法!<.font>

3型文法(正则文法)

正则文法对应有限状态自动机,在2型文法的基础上,规定了
AααBA\rightarrow \alpha|\alpha B (右线性)
oror
AαBαA\rightarrow \alpha|B\alpha (左线性)

  • 3型文法规则表示形式高度受限制,这使得正则语言可以用有限状态自动机程序做高效分析。

  • 有限状态自动机有若干状态,其中肯定有一个起始状态,并至少有一个结束状态

  • 有限状态自动机的输入会导致状态变化,并在到达目标状态时停机

  • 有限状态自动机以文法左部的非终结符指示当前状态,右部的终结符作为输入,终结符后的非终结符(右线性)就是自动机到达的下一状态 —— 状态原来就是非终结符 !

  • 如果输入结束并且此时自动机处于结束状态,则输入就作为一个合法语句被接受,否则输入语句就是非法语句

  • 虽然正则文法简单,但是规则限制太多,无法用于描述自然语言

BNF

::=::= 是定义
| 是或
<    >< \;\;> 是用于括起非终结符

EBNF

扩展BNF是描述编程语言和形式语言的正规方式的上下文物管文法的元语法符号表示法
(    )( \;\;) 内看做一项
  .    \;.\;\; 是一条生成规则的结束
{    }\{\;\; \} 内是零次或任意多次的项
[    ]0[\;\; ] 内是0次或一次的项
"    ""\;\;" 内是终结符

  • 要用当然都是用的EBNF啦,BNF只有三个怎么用?

LL

LL分析器是一种自顶向下上下文无关语法分析器。第一个L是Left to right,第二个L是Leftmost derivation,即分析中使用最左推导。
使用LL分析的就是LL文法!

LL(k)

LL(k)是向前看k个符号才可以决定如何推导,即选择哪个产生式!

在语句的推导过程中,某个阶段可能出现有多个产生式可以选择的情况,如果选择了某个产生式,并且在之后的过程中出错,那么就要进行回溯!!!

为了不进行回溯,需要进行预测,即向前看k个符号来决定选择哪个产生式!

——所以,不会回溯的文法就是LL(k)文法!

  \;
  \;
  \;
下面是几个不同的LL(k):
LL(0)<A>::=<b>LL(0): <A > ::= <b > 不需要向前看(不需要选择),当前的符号只有一个
LL(1)<A>::=<b><c><d>LL(1):<A > ::= <b >|<c >|<d > 需要向前看一个,才能决定选择哪个
LL(2)<A>::=<bd><c><bc>LL(2):<A > ::= <bd >|<c >|<bc > 需要向前看两个,才能决定选择哪个

  \;
  \;

FIRST集

G=(VN,VT,P,S)G = (V_N,V_T,P,S)是上下文无关文法, FIRST(X)={aXaβ,aVT,  X,β    V}    Xε,FIRST(X)=\{ a| X \Rightarrow ^{\ast} a\beta ,a \in V_T ,\; X, \beta \; \in\;V^*\} \;\;。若 X \Rightarrow ^{\ast} \varepsilon , 则规定 εFIRST(X)\varepsilon \in FIRST(X),称 FIRST(X)FIRST(X)XX的开始符号集或者首字符集!!!

X有若干的产生式,在这些产生式中部分产生式的右部的第一个字符是终结符,则这个终结符就是FIRST集中的元素!!!

计算FIRST集

计算FIRST集就是找出属于FIRST集的元素(话说,这一通集合计算后,就是集合论集大拿了吧)

  1. if  XVT,so  FIRST(X)=Xif \; X\in V_T, so\; FIRST(X)={X}
  2. if  XVN,and  Xa...,  aVT,so  aFIRST(x)if \; X\in V_N, and\; X\rightarrow a...,\;a\in V_T,so\; a\in FIRST(x)
  3. if  XVN,Xϵ,so  ϵFIRST(X)if \; X\in V_N,X\rightarrow \epsilon,so\;\epsilon \in FIRST(X)
  4. XVN;Y1,Y2,...,YiVN,X \in V_N; Y_1,Y_2,...,Y_i\in V_N,且有产生式XY1Y2...YiX\rightarrow Y_1 Y_2 ... Y_{i}都可以ϵ\Rightarrow ^* \epsilon 时(1in1\leq i\leq n),则 FIRST(X)=FIRST(Y1)FIRST(Y2)......FIRST(Yi)FIRST(X)=FIRST(Y_1)\bigcup FIRST(Y_2)\bigcup ... ...\bigcup FIRST(Y_i)
  5. 当(4)中所有的Yiϵ,(i=1,2,...,n),Y_i\Rightarrow ^*\epsilon ,(i=1,2,...,n),FIRST(X)=FIRST(Y1)FIRST(Y2)......FIRST(Yn){ϵ}FIRST(X)=FIRST(Y_1)\bigcup FIRST(Y_2)\bigcup ... ...\bigcup FIRST(Y_n)\bigcup \{\epsilon\}
  6. 左右都只有一个符号的话,左右的FIRSTFIRST集等价
  7. 遇到右部第一个符号是非终结符时,需要迭代进去!!!

  \;
  \;

FOLLOW集

G=(VN,VT,P,S)G = (V_N,V_T,P,S)是上下文无关文法,XVN,SX\in V_N,S是开始符号,FOLLOW(X)={aSμXβ,FOLLOW(X) = \{ a|S\Rightarrow ^{\ast} \mu X \beta, 并且 aVT,aFIRST(β),μVT,βV+}a\in V_T,a\in FIRST(\beta),\mu\in V_T^*,\beta \in V^+ \} ,
SμXβ,S\Rightarrow \mu X \beta,并且 βε,\beta \Rightarrow ^* \varepsilon,#FOLLOW(X)\# \in FOLLOW(X)

  • #\#作为输入串的结束符——因为FOLLOW是空嘛,所以自然要结束了!!!

X是产生式的右部,前面的符号是终结符或空,后面的符号中的第一个符号就是X的FOLLOW

计算FOLLOW集

  1. SS为文法开始符号,{#}\{\#\}加入FOLLOW(S)FOLLOW(S)
  2. AaBβA\rightarrow aB\beta是一个产生式,则把FIRST(β)FIRST(\beta)的非空元素加入FOLLOW(B)FOLLOW(B)中。如果βε\beta \Rightarrow ^* \varepsilon,则把FOLLOW(A)FOLLOW(A)也加入FOLLOW(B)FOLLOW(B)
  3. 反复使用(2)直到每个非终结符的FOLLOWFOLLOW集不再增大为止

  \;
  \;

SELECT集

有上下文无关文法的产生式Xa,XVN,aVX \rightarrow a,X\in V_N,a\in V^*
aε,SELECT(Xa)=FIRST(X)如果 a\nRightarrow ^* \varepsilon,则SELECT(X\rightarrow a)=FIRST(X)
aε,SELECT(Xa)=(FIRST(a){ε})FOLLOW(X)如果a\Rightarrow ^* \varepsilon,则SELECT(X\rightarrow a)=(FIRST(a) - \{\varepsilon\} )\bigcup FOLLOW(X)

SELECT(Xa)SELECT(X\rightarrow a)要么是XX右部第一个符号,要么是X跟着的符号中的第一个符号

计算SELECT集

有产生式AaA\rightarrow a,这个aa就是单个符号,不是几个符号的或!!!

  1. FIRST(a)FIRST(a)
  2. εFIRST(a),\varepsilon\notin FIRST(a),则令SELECT(Aa)=FIRST(a)SELECT(A\rightarrow a)=FIRST(a),否则求FOLLOW(A),FOLLOW(A),SELECT(Aa)=FIRST(a)FOLLOW(A)SELECT(A\rightarrow a)=FIRST(a)\bigcup FOLLOW(A)

  \;
  \;

计算三种集合的例子

有下面几个产生式
ETEE\rightarrow TE'
E+TEεE'\rightarrow +TE'|\varepsilon
TFTT\rightarrow FT'
TFTεT'\rightarrow *FT'|\varepsilon
Fid(E)F\rightarrow id|(E)
求这几个产生式的三种集合!

:解:

求FIRST集

1)先看看简单的右部第一个符号是终结符的产生式
FIRST(F)={id,(}FIRST(F) = \{id,(\}
FIRST(T)={,ε}FIRST(T')=\{*,\varepsilon\}
FIRST(E)={+,ε}FIRST(E')=\{+,\varepsilon\}
2)再看右部第一个符号是终结符的产生式
EE的右部第一个是TT,TT的右部第一个是FF
由于FF不能推导出ε\varepsilon,所以 FIRST(T)=FIRST(F)={id,(}FIRST(T)=FIRST(F)=\{id,(\}
由于TT不能推导出ε\varepsilon,所以 FIRST(E)=FIRST(T)={id,(}FIRST(E)=FIRST(T)=\{id,(\}

求FOLLOW集

1)先看EEEE后面的第一个符号只有{)}\{)\}, 则FOLLOW(E)={),#}FOLLOW(E)=\{),\#\}
2)再看EE'EE'后面的第一个符号是空, 即EεE'\rightarrow \varepsilon,则FOLLOW(E)=FOLLOW(E)={),#}FOLLOW(E')=FOLLOW(E)=\{),\#\}
3)TT后面第一个符号是EE',则FOLLOW(T)FOLLOW(T)要算上FIRST(E)FIRST(E'),还有EεE' \rightarrow \varepsilon,则FOLLOW(E)FOLLOW(E')该加入FOLLOW(T)FOLLOW(T)中,则FOLLOW(T)={+,),#}FOLLOW(T)=\{+,),\#\}
4)TT'后面是空, 则FOLLOW(T)=FOLLOW(T)={+,),#}FOLLOW(T')=FOLLOW(T)=\{+,),\#\}
5)FF后面是TT',则FIRST(T)FIRST(T')的非空元素该加入FOLLOW(F)FOLLOW(F)。由于TεT' \rightarrow \varepsilon,则FOLLOW(T)FOLLOW(T')要加入FOLLOW(f)FOLLOW(f),则FOLLOW(F)={,+,),#}FOLLOW(F)=\{*,+,),\#\}

求SELECT集

select处理的式子右部必须只有一个符号

1)因为FIRST(TE)FIRST(TE')有空,所以SELECT(ETE)=FIRST(E)={id,(}SELECT(E\rightarrow TE')=FIRST(E)=\{id,(\}

2)因为FIRST(+TE)FIRST(+TE')有空,所以SELECT(E+TE)=FIRST(+TE)={+}SELECT(E'\rightarrow +TE')=FIRST(+TE') =\{ +\}

3)因为FIRST(ε)FIRST(\varepsilon)是空,所以SELECT(Eε)=(FIRST(ε){ε})FOLLOW(E)={),#}SELECT(E'\rightarrow \varepsilon)=(FIRST(\varepsilon) - \{\varepsilon\})\bigcup FOLLOW(E')=\{ ),\#\}

4)因为FIRST(FT)FIRST(FT')有空,所以SELECT(TFT)=FIRST(FT)={id,(}SELECT(T\rightarrow FT')=FIRST(FT')=\{id,(\}

5)因为FIRST(FT)FIRST(*FT')有空,所以SELECT(TFT)=FIRST(FT)={}SELECT(T'\rightarrow *FT')=FIRST(*FT') =\{ *\}

6 )因为FIRST(ε)FIRST(\varepsilon)是空,所以SELECT(Tε)=(FIRST(ε){ε})FOLLOW(T)={+,),#}SELECT(T'\rightarrow \varepsilon)=(FIRST(\varepsilon) - \{\varepsilon\})\bigcup FOLLOW(T')=\{ +,),\# \}

7)因为FIRST(id)FIRST(id)有空,所以SELECT(Fid)=FIRST(id)={id}SELECT(F\rightarrow id)=FIRST(id)=\{ id \}

8)因为FIRST((E))FIRST( (E) )有空,所以SELECT(F(E))=FIRST((E))={(}SELECT(F\rightarrow (E) )=FIRST( (E) )=\{ (\}

  \;
  \;

LL(1)(常用)

一个上下文无关文法是LL(1)LL(1)文法的充分必要条件是对每个非终结符XX的两个不同产生式XaXβ,X\rightarrow a,X\rightarrow \beta,满足SELECT(Xa)SELECT(Xβ)=ϕ,a,βεSELECT(X\rightarrow a) \bigcap SELECT(X\rightarrow \beta) = \phi ,其中a,\beta 不同时 \Rightarrow ^* \varepsilon

  • LL(1)文法既不是二义性的,也不含左递归,对LL(1)文法的所有句子均可进行确定的自顶向下语法分析

上下文无关文法 + 相同左部的产生式的SELECT集为空 = LL(1)文法
再看这个例子LL(1)<A>::=<b><c><d>LL(1):<A > ::= <b >|<c >|<d >,同样的左部,可以有三个不同的右部,只要保证每个右部的第一个符号不是相同的,那么就能向前看一个就能选择出产生式!!!

递归子程序法

确定的自顶向下分析方法有两种:预测分析法和递归子程序法。

递归子程序法要求文法满足LL(1),实现思想是对文法中每个非终结符编写一个递归过程,每个过程的功能是识别由该非终结符推出的串,当某个非终结符的产生式有多个候选式时,能够按LL(1)形式可唯一地确定选择某个候选进行推导。
由于递归子程序法对每个过程可能存在直接或间接的递归调用,在退出之前可能又被调用。所以,通常在入口处需要保存现场,出口出恢复现场!!!这通常使用栈来实现。

1.语法定义举例(递归子程序法)

<program>      				 ::=    <statement> { statement  } "#"
<statement>    				 ::=    <expression> "\n"
<expression>                 ::=    <multiplicative_expression> 
								{  "+"  <multiplicative_expression>  | "-"  <multiplicative_expression>  }
<multiplicative_expression>  ::=	<primary_expression> 
								{  "*"   <primary_expression> | "/" <primary_expression> }														
<primary_expression>		 ::=    <integer> | "(" <expression> ")"
<integer>   				 ::=    "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" 

2.语法描述图举例(递归子程序法)

在这里插入图片描述

3.构造语法分析程序举例(递归子程序法)

void main(){
	get_token();
	program();
}
void program(){
	while(token != '#')
		statement();
}
void statement(){
	if(token != '\n')expression();
	else get_token();
}
void expression(){
	multiplicative_expression();
	if(token == '+' || token =='-'){
		get_token();
		multiplicative_expression();
	}
}
void multiplicative_expression(){
	primary_expression();
	if(token =='*' || token =='/'){
		get_token();
		primary_expression();
	}
}
void primary_expression(){
	primary_expression();
	if(token == INTEGER)get_token();
	else if(token=='('){
		get_token();
		expression();
		get_token();
		if(token !=')')error("lack of right parenthesis");
	}
	else error("exception");
}

  \;
  \;
  \;

文法等价

根据前面的分析可知,LL(1)文法中,两个相同左部的产生式的几个候选式的第一个符号不可能是相同的,即候选式没有左公共因子!因为如果有的话,那么这个文法至少是LL(k),k2LL(k),k\geq 2
因为要求FIRSTFIRST集,所以左因子肯定不能递归,不然就没完没了了!

1.提取左边的公共因子

如果文法中有AaβaγA\rightarrow a\beta | a \gamma的产生式,这导致了相同产生式的右部的FIRSTFIRST集相交,SELECT(Aaβ)SELECT(Aaγ)ϕSELECT(A\rightarrow a\beta)\bigcap SELECT(A\rightarrow a\gamma) \neq \phi

解:
现在有AaβaγA\rightarrow a\beta | a \gamma
=>Aa(βγ)=> A\rightarrow a(\beta | \gamma )
引入AA'
=>AaA,Aβγ=> A\rightarrow aA',A\rightarrow \beta | \gamma
写成更一般的形式
=>AaA,Aβ1β2...βn=> A \rightarrow aA',A'\rightarrow \beta _1 |\beta _2| ... |\beta _n
如果β1β2...βn\beta _1 |\beta _2| ... |\beta _n中仍然含有左公共因子,就要再次进行提取,反复知道所有的产生式都没用左公共因子为止!!!

2.消除左递归

一个文法含有下列形式的产生式之一时:

  1. AAβ  ,  AVN,βVA\rightarrow A\beta\;,\;A\in V_N,\beta \in V^*
  2. ABβ,BAa  ,  ABVN,aβVA\rightarrow B\beta,B\rightarrow Aa \;,\; A、B\in V_N,a、\beta \in V^*

则称该文法是左递归的!
一个文法是左递归时,不能采用自顶向下分析法。

消除左递归的方法:
1) 消除直接左递归,把直接左递归改写为右递归,比如对文法
G:SSa,SbG:S\rightarrow Sa,S\rightarrow b
改写成
G:SbS,SaSεG:S\rightarrow bS',S'\rightarrow aS' | \varepsilon

S最后只有一个b,说明S串第一个符号就是b
S能一直递归,并提出一个a,则说明S串为baaaaa…aaa
S -> bS’,后面的aaaa…aaa可以当做S’.然后因为要求是去除左递归,所以这里就用右递归S’->aS’
最后,S’也会迭代完,则S’->ε\varepsilon

一般情况下(多个具有相同左部的左递归产生式),假定关于AA的全部产生式为
AAa1Aa2......Aanβ1β2......βnA\rightarrow Aa_1 | Aa_2|......|Aa_n|\beta _1|\beta _2|......|\beta _n,其中ai(1im)a_i(1\leq i\leq m)不等于空,βj(1jn)\beta _j(1\leq j\leq n)不以AA开头
消除左递归后改写成
Aβ1Aβ2A......βnAA\rightarrow \beta _1A' | \beta _2A' | ......|\beta _nA'
Aa1Aa2A......anAεA'\rightarrow a_1 A'|a_2 A'|......|a_n A'|\varepsilon

和上面的例子同理,左递归的变成右递归,原来左递归后面的终结符放到前面来(1)
右部是终结符的也变成右递归,终结符放在前面(2)

2)消除间接左递归。先将间接左递归变成直接左递归,然后按(1)操作

参考:《自己动手写编译器、链接器》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章