【编译原理】Python语法分析LL(1)、LR(1)
目錄
一、實驗目的
二、實驗任務
三、實驗原理
1? LL(1)文法
2? LR文法
四、實驗過程
1 ?LL(1)文法
2? LR文法
五、實驗結果
1 ?LL(1)文法
2? LR(0)文法
3 ?LR(1)文法
參考資料
附錄
1 LL(1)代碼
2 LR(0)文法
3 ?LR(1)代碼
一、實驗目的
1、掌握非遞歸下降的預測分析;
2、了解如何使用Yacc、bision等工具完成語法分析
二、實驗任務
1、針對書上第三章中的表達式文法,采用LL(1)、LR(1)進行分析(必做),不需要做詞法分析,輸入為記號流??梢允止嬙祛A測分析表,但鼓勵自動生成。
2、使用Yacc或bision等工具,實現對表達式文法的語法分析析(附加)。
三、實驗原理
1? LL(1)文法
任何兩個產生式A ?a | b 都滿足下列條件:
(1)FIRST(a ) ? FIRST(b ) = ?;
(2)若b T* e ,那么FIRST(a) ? FOLLOW(A) = ?。
把滿足這兩個條件的文法稱為LL(1)文法。先定義兩個和文法有關的函數FIRST(a ) = {a | a T* a…, a ? VT},特別是,a T* e時,規定e ? FIRST(a ) ;FOLLOW(A) = {a | S T* …Aa…,a?VT},如果A是某個句型的最右符號,那么$屬于FOLLOW(A)。
Step 1:按產生式順序來,從開始符找起;
Step 2:如果右部的串首為終結符,則直接將該終結符填入左部非終結符的First集中;
Step 3:如果右部的串首為非終結符,則左部非終結符的First集等價于串首非終結符的First集。因而,需要利用Step 2和Step 3繼續尋找串首非終結符的First集。
Step 1:按產生式順序來,從開始符找起(開始符的Follow集必定包含$);
Step 2:從所有產生式右部尋找目標非終結符,若其后緊跟終結符,則將終結符填入目標非終結符的Follow集。特別地,若其后緊跟$,則目標非終結符的Follow集等價于產生式左部非終結符的Follow集。
Step 3:從所有產生式右部尋找目標非終結符,若其后緊跟非終結符,則將該非終結符的First集元素填入目標非終結符的Follow集。特別地,若該非終結符的First集元素中包含e,則需針對e情況時做特殊處理,即目標非終結符的Follow集等價于產生式左部非終結符的Follow集。
LL(1)文法有一些明顯的性質:沒有公共左因子;不是二義的;不含左遞歸。
(1)對文法的每個產生式A ? a ,執行(2)和(3);
(2)對FIRST(a)的每個終結符a,把A ? a 加入M[A, a];
(3)如果e在FIRST(a)中,對FOLLOW(A)的每個終結符b(包括$),把A ? a加入M[A, b];
(4)M中其它沒有定義的條目都是error。
2? LR文法
LR分析器的模型如圖所示,它包括輸入、輸出、棧、驅動程序和含動作和轉移兩部分的分析表。驅動程序對所有的LR分析方法都一樣,不同的分析方法構造的分析表不同。分析程序每次從輸入緩沖區讀一個符號,它使用棧存儲形式為s0X1s1X2s2?Xmsm 的串,sm 在棧頂。Xi 是文法符號,si是叫做狀態的符號,狀態符號概括了棧中它下面部分所含的信息。棧頂的狀態符號和當前的輸入符號用來檢索分析表,以決定移進—歸約分析的動作。
圖 3.1:LR分析器的模型
2.1? LR(0)文法
文法G的LR(0)項目(簡稱項目)是在右部的某個地方加點的產生式。如產生式A→XYZ對應有四個項目:
A→·XYZ
A→X·YZ
A→XY·Z
A→XYZ·
產生式A→ε只有一個項目A→·和它對應。直觀地講,項目表示在分析過程的某一點,已經看見了產生式的多大部分(點的左邊部分)和下面希望看見的部分。
2.2? LR(1)文法
讓狀態含有更多的信息,使之能夠剔除無效歸約是完全可能的。定義項目,使之包含一個終結符作為第二個成分,這樣就把更多的信息并入了狀態。項目的一般形式也就成了[A→α·β,a],其中A→αβ是產生式,a是終結符號或$,這種項目叫做LR(1)項目,1是第二個成分的長度,這個成分叫做項目的搜索符。搜索符對β非空的項目[A→α·β,a]是不起作用的,但對形式為[A→α·,a]的項目,它表示只有在下一個輸入符號是a時,才能要求按A→α歸約。這樣,分析器只有在輸入符號是a時才按A→α歸約,其中[A→α·,a]在棧頂狀態的LR(1)項目集中。這樣的a的集合是FOLLOW(A)的子集,完全可能是真子集。我們說LR(1)項目[A→α·β,a]對活前綴γ是有效的,如果存在著推導S=>*rmδAw=>rmδαβw,其中:
(1)? γ=δα;
(2) a是w的第一個符號,或者w 是ε且a是$。
在LR(1)文法中,棧中的文法符號總是形成一個活前綴,分析表的轉移函數本質上是識別活前綴的DFA,棧頂的狀態符號包含了確定句柄所需要的一切信息,是已知的最一般的無回溯的移進?歸約方法,能及時發現語法錯誤,但是,手工構造分析表的工作量太大。
LR分析法的規約過程是規范推倒的逆過程,所以LR分析過程是一種規范規約的逆過程,L表示從左到右掃描輸入串, R表示最左規約(即最右推導的逆過程),括號中的1表示向右查看輸入符號數為1。LR(1)項目可以看成兩個部分組成,一部分和LR(0)項目相同,這部分成為心,另一部分為向前搜索符集合。所以只有當面臨的輸入符屬于向前搜索符的集合,才做規約動作,其他情況均出錯。LR(1)方法恰好解決LR(0)(1)方法在某些情況下存在的無效規約問題。
四、實驗過程
1 ?LL(1)文法
LL(1)文法的分析模型如圖4.1所示,課本上要分析的文法(3.8)如下:
E→TE′
E′→+TE′|ε
T→FT′
T′→*FT′|ε
F→(E)|id
圖4.1:LL(1)文法的分析模型
(1)構造LL(1)預測分析表,按照實驗原理中描述的流程,最終分析表如下表所示。
表4.1:LL(1)預測分析表
在實際編程中,用字典進行存儲,其中P代表E′,p代表T′,e代表e,如下所示:
| #Epie=P;Tpie=p;yimuxitong=e dicM={ ??? "E":{"i":"TP","C":"TP"}, ??? "P":{"+":"+TP",")":"e","$":"e"}, ??? "T":{"i":"Fp","(":"Fp"}, ??? "p":{"+":"e","*":"*Fp",")":"e","$":"e"}, ??? "F":{"i":"i","(":"(E)"} } | 
(2)按照LL(1)文法分析的偽代碼進行編程,具體代碼詳見附錄1.
圖4.2:LL(1)文法的偽代碼
2? LR文法
LR(1)文法的分析模型如圖3.1所示.
2.1 ?LR(0)文法
利用LR(0)課本上要分析的文法(3.11)如下:
E->E+T
E->T
T->T*F
T->F
F->(E)
F->i
(1)構造LR(0)預測分析表,按照實驗原理中描述的流程,最終分析表如下表所示。
表4.2:LR(0)預測分析表
在實際編程中,用字典進行存儲,如下所示:
| action={0: {'i': 's5', '(': 's4'}, ?1: {'+': 's6', '$': 'acc'}, ?2: {'+': 'r2', '*': 's7', ')': 'r2', '$': 'r2'}, ?3: {'+': 'r4', '*': 'r4', ')': 'r4', '$': 'r4'}, ?4: {'i': 's5', '(': 's4'}, ?5: {'+': 'r6', '*': 'r6', ')': 'r6', '$': 'r6'}, ?6: {'i': 's5', '(': 's4'}, ?7: {'i': 's5', '(': 's4'}, ?8: {'+': 's6', ')': 's11'}, ?9: {'+': 'r1', '*': 's7', ')': 'r1', '$': 'r1'}, ?10: {'+': 'r3', '*': 'r3', ')': 'r3', '$': 'r3'}, ?11: {'+': 'r5', '*': 'r5', ')': 'r5', '$': 'r5'}} goto={0: {'E': 1, 'T': 2, 'F': 3}, ?4: {'E': 8, 'T': 2, 'F': 3}, ?6: {'T': 9, 'F': 3}, ?7: {'F': 10}} | 
(2)按照LR(0)文法分析的偽代碼進行編程,具體代碼詳見附錄2.
圖4.3:LR(0)文法的偽代碼
2.2 ?LR(1)文法
課本上要分析的文法(3.13)如下:
對下列文法,用LR(1)分析法對任意輸入的符號串進行分析:
E->S
S->BB
B->bB|a
程序的流程圖如圖所示,其中輸入以#結束的符號串(包括a、b、#),如:abb#。此文法采用C++進行編程,對應文法分析表Action的存儲結構為字符型二維數組,Goto的存儲結構為整數型二維數組,如下所示:
圖4.4: LR(1)分析流程圖
圖4.5:LR(1)分析表的存儲結構示意圖
五、實驗結果
1 ?LL(1)文法
程序識別出文法的非終結符VN集合為:['E', 'P', 'T', 'p', 'F'];終結符VT集合為['(', 'C', '+', ')', '*', 'i', '$', 'e'],其中P代表E′,p代表T′,e代表e。輸入符號串i*i+i,分析結果如下圖所示,經過17步分析,最終判斷該句子屬于該文法,分析結束。當然,如果輸入i*+i,不符合該文法的句子,結果如圖5.2所示,最終判斷該句子不屬于該文法。說明程序編寫正確。
圖5.1: 句子“i*i+i”的LL(1)分析過程
圖5.2: 句子“i*+i”的LL(1)分析過程
2? LR(0)文法
輸入句子“i*i+i$”,采用LR(0)文法進行分析的結果如下,經過14步分析,最終判斷該句子屬于該文法,分析結束;當然,如果輸入i*+i,不符合該文法的句子,結果如圖5.4所示,最終判斷該句子不屬于該文法。程序編寫正確。
圖5.3: 句子“i*i+i”的LR(0)分析過程
圖5.4: 句子“i*+i”的LR(0)分析過程
3 ?LR(1)文法
輸入句子“abb#”,采用LR(1)文法進行分析的結果如下,經過8步分析,最終判斷該句子屬于該文法,分析結束;輸入句子“abbab#”,采用LR(1)文法進行分析的結果如下,經過6步分析,最終判斷該句子不屬于該文法,分析結束。
圖5.5:LR(1)文法的分析過程
參考資料
附錄
1 LL(1)代碼
| # -*- coding: utf-8 -*- """ Created on Thu Apr 16 10:41:01 2020 @author: 小果果學長 """ #分析表M #dicM={ #??? "S":{"(":"A", ")":"A"}, #??? "A":{"(":"CB", ")":"CB" }, #??? "B":{"i":"iCB","*":"e", "$":"e"}, #??? "C":{"(":"ED", ")":"ED" }, #? ??"D":{"i":"e","+":"+ED","*":"e", "$":"e"}, #??? "E":{"(":"(", ")":")A*" } #} #Epie=P;Tpie=p;yimuxitong=e dicM={ ??? "E":{"i":"TP","C":"TP"}, ??? "P":{"+":"+TP",")":"e","$":"e"}, ??? "T":{"i":"Fp","(":"Fp"}, ??? "p":{"+":"e","*":"*Fp",")":"e","$":"e"}, ??? "F":{"i":"i","(":"(E)"} } #非終結符 VN=[] for item in dicM.keys(): ??? VN.append(item); print("非終結符VN集合為:{0}".format(VN)) #終結符 VT=[] # print(len(dicM)) for item in dicM.values(): ??? for it in item:#遍歷key值 ??????? VT.append(it) VT=list(set(VT)) VT.append("e") print("終結符VT集合為{0}:".format(VT)) #翻轉字符串,返回字符串 def convertStr(arg): ??? conarg=arg[::-1] ??? return conarg #找產生式,返回產生式右部 def findCSS(argS,argstr): ??? if argS in dicM.keys() : ??????? temp_value=dicM[argS] ??????? if argstr in temp_value.keys(): ??????????? temp=temp_value[argstr] ??????????? return temp ??? return "" #截取列表 除去最后一個元素 def substr(arg): ??? # print(arg,"fdsfs") ??? Listarg=list(arg) ??? le=len(Listarg) ??? args=Listarg[0:le-1] ??? return args def addS(arg,args): ??? Listargs=list(args) ??? for i in arg: ??????? Listargs.append(i) ??? return? Listargs #strString=input("請輸入要分析的符號串:")#輸入串 strString="i*i+i" str=list(strString) str.append("$") COUNT=0#步驟 #符號棧 S=["$"] S.append(VN[0]) # print(S) CSRight="" print("步驟\t符號棧S[i]\t\t\t輸入串str[j]\t\t\t產生式") while len(S)!=0: ??? COUNT += 1 ??? ch = str[0] ??? CSRight0 = findCSS(S[-1], str[0]) ??? if CSRight0!="" and S[-1] not in VT: ??????? print("%s\t%s\t\t\t\t%s\t\t\t\t%s" % ( COUNT, "".join(S).center(10), "".join(str).center(10),S[-1] + "->" +CSRight0)) ??? elif? CSRight0 in VT or S[-1]? in VT: ??????? print("%s\t%s\t\t\t\t%s" % ( COUNT, "".join(S).center(10), "".join(str).center(10))) ??? elif CSRight0=="": ??????? print("\033[0;31m%s\033[0m" % "該句子不是該文法!") ??????? break ??? CHS = S.pop() ??? CSRight = findCSS(CHS, str[0]) ??? if CHS not in VT? :#如果棧頂元素不是終結字符 ??????? if CSRight!="": ??????????? if CSRight[0] in VN : ??????????????? temp = convertStr(CSRight) ??????????????? S = addS(temp,S) ?? ?????????elif CSRight[0] in VT and CSRight[0]!="e": ??????????????? temp = convertStr(CSRight) ??????????????? S = addS(temp, S) ??????????? elif CSRight[0]=="e" and CSRight[0] in VT: ??????????????? pass ??????? elif CSRight=="": ??????????? pass ??? elif CHS in VT: ??????? if ch==CHS: ??????????? if CHS=="$": ??????????????? # print("該句子屬于該文法!") ??????????????? print("\033[1;31;40m該句子屬于該文法!\033[0m") ??????????? elif CHS!="$": ??????????????? str = str[1:] ??????? elif ch!=CHS: ?? ?????????if CHS=="$": ??????????????? print("\033[0;31m%s\033[0m" % "該句子不是該文法!") ??????????????? pass | 
2 LR(0)文法
2.1 LR(0)代碼
| #使用已經建立好的分析表 action={0: {'i': 's5', '(': 's4'}, ?1: {'+': 's6', '$': 'acc'}, ?2: {'+': 'r2', '*': 's7', ')': 'r2', '$': 'r2'}, ?3: {'+': 'r4', '*': 'r4', ')': 'r4', '$': 'r4'}, ?4: {'i': 's5', '(': 's4'}, ?5: {'+': 'r6', '*': 'r6', ')': 'r6', '$': 'r6'}, ?6: {'i': 's5', '(': 's4'}, ?7: {'i': 's5', '(': 's4'}, ?8: {'+': 's6', ')': 's11'}, ?9: {'+': 'r1', '*': 's7', ')': 'r1', '$': 'r1'}, ?10: {'+': 'r3', '*': 'r3', ')': 'r3', '$': 'r3'}, ?11: {'+': 'r5', '*': 'r5', ')': 'r5', '$': 'r5'}} goto={0: {'E': 1, 'T': 2, 'F': 3}, ?4: {'E': 8, 'T': 2, 'F': 3}, ?6: {'T': 9, 'F': 3}, ?7: {'F': 10}} gramma=open('wx.txt').readlines() i=0 while i<len(gramma): #去掉'->'符號 ??? gramma[i]=gramma[i][0:1]+gramma[i][3:len(gramma[i])-1] ??? i+=1 #sen='i*i+i$' sen='i*+i$' ip=0#sen的指針 stack=['$',0] a=sen[ip] while True: ??? print(stack, end="") ??? print("\t",end="") ??? s=stack[len(stack)-1] #s為棧頂狀態 ??? if? a in action[s]:#存在action[s][a] ??????? if action[s][a]=='acc': #若為acc 則成功,并結束 ??????????? ??????????? print("\033[1;31;40mAccept,該句子屬于該文法!\033[0m") ??????????? break ??????? elif action[s][a][0]=='s':#若為 si 則把a和i依次入棧 a指向下一個 ??????????? print('移進'+action[s][a]) ??????????? t=int(action[s][a][1]) ??????????? stack.append(a) ??????????? stack.append(t) ??????????? ip+=1 ??????????? a=sen[ip] ??????? elif action[s][a][0]=='r':#若為ri i為第i個文法(gramma[i-1]) 則?;赝?*gramma[i-1]表達式表達式右端的長度 ??????????? print('規約'+gramma[int(action[s][a][1])-1][0:1]+'->'+gramma[int(action[s][a][1])-1][1:]) ??????????? size=len(gramma[int(action[s][a][1])-1])-1 #rj 對應的第j個產生式右端的長度 ??? ????????j=0 ??????????? while j<2*size: ??????????????? stack.pop() ??????????????? j+=1 ??????????? t=stack[len(stack)-1]#t為現在棧的狀態 ??????????? stack.append(gramma[int(action[s][a][1])-1][0])#表達式左端入棧 ??????????? stack.append(goto[t][gramma[int(action[s][a][1])-1][0]])#goto[t,表達式左端]入棧 ??? else: ??????????? #調用錯誤恢復例程 ??????????? print('error') ??????????? break; | 
2.2 wx.txt附件
*注:最后一行有換行。
| E->E+T E->T T->T*F T->F F->(E) F->i | 
3 ?LR(1)代碼
| #include<stdio.h> #include<string.h> char *action[10][3]= {"S3#","S4#",NULL,?????????? /*ACTION表*/ ????????????????????? NULL,NULL,"acc", ????????????????????? "S6#","S7#",NULL, ????????????????????? "S3#","S4#",NULL, ????????????????????? "r3#","r3#",NULL, ????????????????????? NULL,NULL,"r1#", ????????????????????? "S6#","S7#",NULL, ????????????????????? NULL,NULL,"r3#", ????????????????????? "r2#","r2#",NULL, ????????????????????? NULL,NULL,"r2#" ???????????????????? }; int goto1[10][2]= {1,2,?????????????????????????????????????????????? /*QOTO表*/ ?????????????????? 0,0, ?????????????????? 0,5, ?????????????????? 0,8, ?????????????????? 0,0, ?????????????????? 0,0, ?????????????????? 0,9, ?????????????????? 0,0, ?????????????????? 0,0, ?????????????????? 0,0 ????????????????? }; char vt[3]= {'a','b','#'};?????????????????????? /*存放非終結符*/ char vn[2]= {'S','B'};?????????????????????????? /*存放終結符*/ char *LR[4]= {"E->S#","S->BB#","B->bB#","B->a#"};/*存放產生式*/ int a[10]; char b[10],c[10],c1; int top1,top2,top3,top,m,n; int main() { ?????? int g,h,i,j,k,l,p,y,z,count; ?????? char x,copy[10],copy1[10]; ?????? top1=0; ?????? top2=0; ?????? top3=0; ?????? top=0; ?????? a[0]=0; ?????? y=a[0]; ?????? b[0]='#'; ?????? count=0; ?????? z=0; ?????? printf("請輸入表達式\n");//abb# ?????? do { ????????????? scanf("%c",&c1); ????????????? c[top3]=c1; ????????????? top3=top3+1; ?????? } while(c1!='#'); ?????? printf("步驟\t狀態棧\t\t符號棧\t\t輸入串\t\tACTION\tGOTO\n"); ?????? do { ????????????? y=z; ????????????? m=0; ????????????? n=0;????????????????????? /*y,z指向狀態棧棧頂*/ ????????????? g=top; ????????????? j=0; ????????????? k=0; ????????????? x=c[top]; ????????????? count++; ????????????? printf("%d\t",count); ????????????? while(m<=top1) { ???????????????????? /*輸出狀態棧*/ ???????????????????? printf("%d",a[m]); ?????? ????????????? m=m+1; ????????????? } ????????????? printf("\t\t"); ????????????? while(n<=top2) { ???????????????????? /*輸出符號棧*/ ???????????????????? printf("%c",b[n]); ???????????????????? n=n+1; ????????????? } ????????????? printf("\t\t"); ????????????? while(g<=top3) { ???????????????????? /*輸出輸入串*/ ???????????????????? printf("%c",c[g]); ???????????????????? g=g+1; ????????????? } ????????????? printf("\t\t"); ????????????? while(x!=vt[j]&&j<=2) j++; ????????????? if(j==2&&x!=vt[j]) { ???????????????????? printf("error\n"); ???????????????????? return 0; ????????????? } ????????????? if(action[y][j]==NULL) { ???????????????????? printf("error\n"); ???????????????????? return 0; ????????????? }?? else??? strcpy(copy,action[y][j]); ????????????? if(copy[0]=='S') { ???????????????????? /*處理移進*/??? z=copy[1]-'0'; ???????????????????? top1=top1+1; ???????????????????? top2=top2+1; ???????????????????? a[top1]=z; ???????????????????? b[top2]=x; ???????????????????? top=top+1; ???????????????????? i=0; ???????????????????? while(copy[i]!='#') { ??????????????????????????? printf("%c",copy[i]); ??????????????????????????? i++; ???????????????????? } ???????????????????? printf("\n"); ????????????? } ????????????? if(copy[0]=='r') { ???????????????????? /*處理歸約*/??? i=0; ???????????????????? while(copy[i]!='#') { ??????????????????????????? printf("%c",copy[i]); ??????????????????????????? i++; ???????????????????? } ???????????????????? h=copy[1]-'0'; ???????????????????? strcpy(copy1,LR[h]); ???????????????????? while(copy1[0]!=vn[k]) k++; ???????????????????? l=strlen(LR[h])-4; ???????????????????? top1=top1-l+1; ???????????????????? top2=top2-l+1; ???????????????????? y=a[top1-1]; ???????????????????? p=goto1[y][k]; ???????????????????? a[top1]=p; ???????????????????? b[top2]=copy1[0]; ???????????????????? z=p; ???????????????????? printf("\t"); ???????????????????? printf("%d\n",p); ????????????? } ?????? } while(action[y][j]!="acc"); ?????? printf("恭喜你,分析成功!\n"); ?????? return 0; } | 
總結
以上是生活随笔為你收集整理的【编译原理】Python语法分析LL(1)、LR(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 图片征集网站源码_征集提名:2013年卡
- 下一篇: Leetcode刷题——剑指offer_
