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

默认的规则

V N V_N VN是非终结符集合
V T V_T VT是终结符集合
终结符和非终结符的集合是互斥的
一般字母的大写是非终结符,小写是终结符
V = V N ⋃ V T V = V_N \bigcup V_T V=VNVT
G G G是文法/语言
P P P是产生式,也就是推导的式子 a → b c a \rightarrow bc abc
S S S是开始符号
a → b c 是 推 导 b c → a 是 规 约 a\rightarrow bc 是推导 bc \rightarrow a 是规约 abcbca
: : = 和 → ::=和\rightarrow ::=或者其他的箭头符号是一样意思
字 母 后 的 ∗ 字母后的* 表示0个或多个
字 母 后 的 + 字母后的+ +表示1个或多个

0型文法

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

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

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

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

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

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

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

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

3型文法(正则文法)

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

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

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

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

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

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

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

BNF

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

EBNF

扩展BNF是描述编程语言和形式语言的正规方式的上下文物管文法的元语法符号表示法
(      ) 内 看 做 一 项 ( \;\;) 内看做一项 ()
   .      是 一 条 生 成 规 则 的 结 束 \;.\;\; 是一条生成规则的结束 .
{      } 内 是 零 次 或 任 意 多 次 的 项 \{\;\; \} 内是零次或任意多次的项 { }
[      ] 内 是 0 次 或 一 次 的 项 [\;\; ] 内是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):
L L ( 0 ) : < A > : : = < b > LL(0): <A > ::= <b > LL(0)<A>::=<b> 不需要向前看(不需要选择),当前的符号只有一个
L L ( 1 ) : < A > : : = < b > ∣ < c > ∣ < d > LL(1):<A > ::= <b >|<c >|<d > LL(1)<A>::=<b><c><d> 需要向前看一个,才能决定选择哪个
L L ( 2 ) : < A > : : = < b d > ∣ < c > ∣ < b c > LL(2):<A > ::= <bd >|<c >|<bc > LL(2)<A>::=<bd><c><bc> 需要向前看两个,才能决定选择哪个

   \;
   \;

FIRST集

G = ( V N , V T , P , S ) G = (V_N,V_T,P,S) G=(VN,VT,P,S)是上下文无关文法, F I R S T ( X ) = { a ∣ X ⇒ ∗ a β , a ∈ V T ,    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)={ aXaβ,aVT,X,βV}Xε, 则规定 ε ∈ F I R S T ( X ) \varepsilon \in FIRST(X) εFIRST(X),称 F I R S T ( X ) FIRST(X) FIRST(X) X X X的开始符号集或者首字符集!!!

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

计算FIRST集

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

  1. i f    X ∈ V T , s o    F I R S T ( X ) = X if \; X\in V_T, so\; FIRST(X)={X} ifXVT,soFIRST(X)=X
  2. i f    X ∈ V N , a n d    X → a . . . ,    a ∈ V T , s o    a ∈ F I R S T ( x ) if \; X\in V_N, and\; X\rightarrow a...,\;a\in V_T,so\; a\in FIRST(x) ifXVN,andXa...,aVT,soaFIRST(x)
  3. i f    X ∈ V N , X → ϵ , s o    ϵ ∈ F I R S T ( X ) if \; X\in V_N,X\rightarrow \epsilon,so\;\epsilon \in FIRST(X) ifXVN,Xϵ,soϵFIRST(X)
  4. X ∈ V N ; Y 1 , Y 2 , . . . , Y i ∈ V N , X \in V_N; Y_1,Y_2,...,Y_i\in V_N, XVN;Y1,Y2,...,YiVN,且有产生式 X → Y 1 Y 2 . . . Y i X\rightarrow Y_1 Y_2 ... Y_{i} XY1Y2...Yi都可以 ⇒ ∗ ϵ \Rightarrow ^* \epsilon ϵ 时( 1 ≤ i ≤ n 1\leq i\leq n 1in),则 F I R S T ( X ) = F I R S T ( Y 1 ) ⋃ F I R S T ( Y 2 ) ⋃ . . . . . . ⋃ F I R S T ( Y i ) FIRST(X)=FIRST(Y_1)\bigcup FIRST(Y_2)\bigcup ... ...\bigcup FIRST(Y_i) FIRST(X)=FIRST(Y1)FIRST(Y2)......FIRST(Yi)
  5. 当(4)中所有的 Y i ⇒ ∗ ϵ , ( i = 1 , 2 , . . . , n ) , Y_i\Rightarrow ^*\epsilon ,(i=1,2,...,n), Yiϵ,(i=1,2,...,n), F I R S T ( X ) = F I R S T ( Y 1 ) ⋃ F I R S T ( Y 2 ) ⋃ . . . . . . ⋃ F I R S T ( Y n ) ⋃ { ϵ } FIRST(X)=FIRST(Y_1)\bigcup FIRST(Y_2)\bigcup ... ...\bigcup FIRST(Y_n)\bigcup \{\epsilon\} FIRST(X)=FIRST(Y1)FIRST(Y2)......FIRST(Yn){ ϵ}
  6. 左右都只有一个符号的话,左右的 F I R S T FIRST FIRST集等价
  7. 遇到右部第一个符号是非终结符时,需要迭代进去!!!

   \;
   \;

FOLLOW集

G = ( V N , V T , P , S ) G = (V_N,V_T,P,S) G=(VN,VT,P,S)是上下文无关文法, X ∈ V N , S X\in V_N,S XVN,S是开始符号, F O L L O W ( X ) = { a ∣ S ⇒ ∗ μ X β , FOLLOW(X) = \{ a|S\Rightarrow ^{\ast} \mu X \beta, FOLLOW(X)={ aSμXβ, 并且 a ∈ V T , a ∈ F I R S T ( β ) , μ ∈ V T ∗ , β ∈ V + } a\in V_T,a\in FIRST(\beta),\mu\in V_T^*,\beta \in V^+ \} aVT,aFIRST(β),μVT,βV+} ,
S ⇒ μ X β , S\Rightarrow \mu X \beta, SμXβ,并且 β ⇒ ∗ ε , \beta \Rightarrow ^* \varepsilon, βε, # ∈ F O L L O W ( X ) \# \in FOLLOW(X) #FOLLOW(X)

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

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

计算FOLLOW集

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

   \;
   \;

SELECT集

有上下文无关文法的产生式 X → a , X ∈ V N , a ∈ V ∗ X \rightarrow a,X\in V_N,a\in V^* Xa,XVN,aV
如 果 a ⇏ ∗ ε , 则 S E L E C T ( X → a ) = F I R S T ( X ) 如果 a\nRightarrow ^* \varepsilon,则SELECT(X\rightarrow a)=FIRST(X) aε,SELECT(Xa)=FIRST(X)
如 果 a ⇒ ∗ ε , 则 S E L E C T ( X → a ) = ( F I R S T ( a ) − { ε } ) ⋃ F O L L O W ( X ) 如果a\Rightarrow ^* \varepsilon,则SELECT(X\rightarrow a)=(FIRST(a) - \{\varepsilon\} )\bigcup FOLLOW(X) aε,SELECT(Xa)=(FIRST(a){ ε})FOLLOW(X)

S E L E C T ( X → a ) SELECT(X\rightarrow a) SELECT(Xa)要么是 X X X右部第一个符号,要么是X跟着的符号中的第一个符号

计算SELECT集

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

  1. F I R S T ( a ) FIRST(a) FIRST(a)
  2. ε ∉ F I R S T ( a ) , \varepsilon\notin FIRST(a), ε/FIRST(a),则令 S E L E C T ( A → a ) = F I R S T ( a ) SELECT(A\rightarrow a)=FIRST(a) SELECT(Aa)=FIRST(a),否则求 F O L L O W ( A ) , FOLLOW(A), FOLLOW(A), S E L E C T ( A → a ) = F I R S T ( a ) ⋃ F O L L O W ( A ) SELECT(A\rightarrow a)=FIRST(a)\bigcup FOLLOW(A) SELECT(Aa)=FIRST(a)FOLLOW(A)

   \;
   \;

计算三种集合的例子

有下面几个产生式
E → T E ′ E\rightarrow TE' ETE
E ′ → + T E ′ ∣ ε E'\rightarrow +TE'|\varepsilon E+TEε
T → F T ′ T\rightarrow FT' TFT
T ′ → ∗ F T ′ ∣ ε T'\rightarrow *FT'|\varepsilon TFTε
F → i d ∣ ( E ) F\rightarrow id|(E) Fid(E)
求这几个产生式的三种集合!

解 : 解: :

求FIRST集

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

求FOLLOW集

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

求SELECT集

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

1)因为 F I R S T ( T E ′ ) FIRST(TE') FIRST(TE)有空,所以 S E L E C T ( E → T E ′ ) = F I R S T ( E ) = { i d , ( } SELECT(E\rightarrow TE')=FIRST(E)=\{id,(\} SELECT(ETE)=FIRST(E)={ id,(}

2)因为 F I R S T ( + T E ′ ) FIRST(+TE') FIRST(+TE)有空,所以 S E L E C T ( E ′ → + T E ′ ) = F I R S T ( + T E ′ ) = { + } SELECT(E'\rightarrow +TE')=FIRST(+TE') =\{ +\} SELECT(E+TE)=FIRST(+TE)={ +}

3)因为 F I R S T ( ε ) FIRST(\varepsilon) FIRST(ε)是空,所以 S E L E C T ( E ′ → ε ) = ( F I R S T ( ε ) − { ε } ) ⋃ F O L L O W ( E ′ ) = { ) , # } SELECT(E'\rightarrow \varepsilon)=(FIRST(\varepsilon) - \{\varepsilon\})\bigcup FOLLOW(E')=\{ ),\#\} SELECT(Eε)=(FIRST(ε){ ε})FOLLOW(E)={ ),#}

4)因为 F I R S T ( F T ′ ) FIRST(FT') FIRST(FT)有空,所以 S E L E C T ( T → F T ′ ) = F I R S T ( F T ′ ) = { i d , ( } SELECT(T\rightarrow FT')=FIRST(FT')=\{id,(\} SELECT(TFT)=FIRST(FT)={ id,(}

5)因为 F I R S T ( ∗ F T ′ ) FIRST(*FT') FIRST(FT)有空,所以 S E L E C T ( T ′ → ∗ F T ′ ) = F I R S T ( ∗ F T ′ ) = { ∗ } SELECT(T'\rightarrow *FT')=FIRST(*FT') =\{ *\} SELECT(TFT)=FIRST(FT)={ }

6 )因为 F I R S T ( ε ) FIRST(\varepsilon) FIRST(ε)是空,所以 S E L E C T ( T ′ → ε ) = ( F I R S T ( ε ) − { ε } ) ⋃ F O L L O W ( T ′ ) = { + , ) , # } SELECT(T'\rightarrow \varepsilon)=(FIRST(\varepsilon) - \{\varepsilon\})\bigcup FOLLOW(T')=\{ +,),\# \} SELECT(Tε)=(FIRST(ε){ ε})FOLLOW(T)={ +,),#}

7)因为 F I R S T ( i d ) FIRST(id) FIRST(id)有空,所以 S E L E C T ( F → i d ) = F I R S T ( i d ) = { i d } SELECT(F\rightarrow id)=FIRST(id)=\{ id \} SELECT(Fid)=FIRST(id)={ id}

8)因为 F I R S T ( ( E ) ) FIRST( (E) ) FIRST((E))有空,所以 S E L E C T ( F → ( E ) ) = F I R S T ( ( E ) ) = { ( } SELECT(F\rightarrow (E) )=FIRST( (E) )=\{ (\} SELECT(F(E))=FIRST((E))={ (}

   \;
   \;

LL(1)(常用)

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

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

上下文无关文法 + 相同左部的产生式的SELECT集为空 = LL(1)文法
再看这个例子 L L ( 1 ) : < A > : : = < b > ∣ < c > ∣ < d > 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)文法中,两个相同左部的产生式的几个候选式的第一个符号不可能是相同的,即候选式没有左公共因子!因为如果有的话,那么这个文法至少是 L L ( k ) , k ≥ 2 LL(k),k\geq 2 LL(k),k2
因为要求 F I R S T FIRST FIRST集,所以左因子肯定不能递归,不然就没完没了了!

1.提取左边的公共因子

如果文法中有 A → a β ∣ a γ A\rightarrow a\beta | a \gamma Aaβaγ的产生式,这导致了相同产生式的右部的 F I R S T FIRST FIRST集相交, S E L E C T ( A → a β ) ⋂ S E L E C T ( A → a γ ) ≠ ϕ SELECT(A\rightarrow a\beta)\bigcap SELECT(A\rightarrow a\gamma) \neq \phi SELECT(Aaβ)SELECT(Aaγ)=ϕ

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

2.消除左递归

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

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

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

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

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

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

和上面的例子同理,左递归的变成右递归
其实从第一条产生式就可以看出, A A A = β 1 a 1 a 1 . . . ∣ β 2 a 2 a 2 . . . ∣ . . . . . . ∣ β n a n a n . . . . \beta _1 a_1a_1...|\beta _2 a_2a_2...|......|\beta _n a_na_n.... β1a1a1...β2a2a2.........βnanan....
就是以一种终结符开头,以若干个另一种终结符连接的字符串

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

比如有文法 G : G: G:
S → Q c ∣ c S\rightarrow Qc|c SQcc
Q → R b ∣ b Q\rightarrow Rb|b QRbb
R → S a ∣ a R\rightarrow Sa|a RSaa

改成直接左递归 : : :
R R R代入 Q Q Q
Q → R b ∣ b → ( S a ∣ a ) b ∣ b → S a b ∣ a b ∣ b Q\rightarrow Rb|b \rightarrow (Sa|a)b|b\rightarrow Sab|ab|b QRbb(Saa)bbSababb
Q Q Q代入 S S S
S → Q c ∣ c → ( S a b ∣ a b ∣ b ) c ∣ c → S a b c ∣ a b c ∣ b c ∣ c S\rightarrow Qc|c \rightarrow (Sab|ab|b)c|c\rightarrow Sabc|abc|bc|c SQcc(Sababb)ccSabcabcbcc
然后再消除左递归
S → a b c S ′ ∣ b c S ′ ∣ c S ′ S\rightarrow abcS'|bcS'|cS' SabcSbcScS
S ′ → a b c S ′ ∣ ε S'\rightarrow abcS'|\varepsilon SabcSε
R , Q R,Q R,Q就可以不要了

所以,间接转直接的方法就是,先选好开始的S,然后找到最底层的产生式,再逐步代入上一层的产生式…
显然这存在个问题,如果最后剩下两个非终结符相互迭代,就没办法代入了!!!

参考:
《自己动手写编译器、链接器》
编译原理左递归消除?-百度知道

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/105659468