算符優先分析文法
一、寫在前面
算符優先分析文法是一種工具,在編譯的過程中,隸屬於語法分析環節,卻又與中間代碼的生成息息相關,編譯可以分為五個階段:詞法分析、語法分析、語義分析(中間代碼的生成)、代碼優化、目標代碼生成。語法分析是指:在詞法分析基礎上,將單詞符號串轉化為語法單位(語法范疇)(短語、子句、句子、程序段、程序),並確定整個輸入串是否構成語法上正確的程序。也就是說語法分析是檢驗輸入串的語法是否正確,注意這里的語法正確,只是簡單地符合自己定義的規范,而不能檢測出運行時錯誤,比如"X/0",空指針錯誤,對象未初始化等錯誤。在這一個實驗中,我將通過算符優先分析文法這一個工具,在語法分析的時候,順便進行語義分析,也就是識別出語法單位,同時簡要的將識別出的中間代碼進行計算(目標代碼的生成+運行),得到相應的結果,來檢驗自己設計的正確性。可以說題目雖然叫做算符優先分析文法,其實卻是一個貫穿了“詞法分析+語法分析+語義分析+中間代碼優化+目標代碼生成+運行”全過程的一個極具概括性的程序。如果能將這個程序得心應手的完成出來,我相信諸位對編譯原理的掌握也算是爐火純青了。時隔將近兩年再來整理自己以前寫過的實驗報告,還是挺有感慨的,對一件東西感興趣,原來影響還會如此深遠,還記得自己當時連續六個小時全神貫注寫出的實驗報告,現在看看竟然寫了五六十頁,核心內容也有三四十頁,不覺的感慨當年充滿熱情的時代慢慢的竟走出許久~~
二、算符優先分析文法實驗
2.1、任務
實驗目的:
1.了解掌握算符優先分析的基本方法、內容;
2.學會科學思考並解決問題,提高程序設計能力。
實驗內容與要求:
用算符優先分析方法設計一個分析解釋程序,對輸入的賦值語句、輸出語句、清除語句進行詞法分析、語法分析、表達式求值並存儲於指定變量中;若存在錯誤,提示錯誤相關信息。
文法表示:
S→v=E|E?|clear
E→E+T|E-T|T
T→T*F|T/F|F
F→ (E)|v|c
單詞種別碼設計:
= 1
? 2
+ 3
- 4
* 5
/ 6
( 7
) 8
v 9
c 10
clear 11
# 12
N 13
可歸約串語義解釋:
變量歸約;常量歸約;運算歸約;括號歸約;
賦值語句;輸出語句;清除語句。
演示示例:
a=5
b=a+10
b?
b+a*a?
a=a+b
2.2、分析與設計
首先,這是一個很好的題目,從知識的理解、將形式化的東西轉化成具體的過程、編程能力、編程技巧、調試改錯能力等多個方面考察了我們的學習情況。 算符優先文法是一種自下而上的文法分析方法,這種方法的用處十分廣泛,雖然有的文法不能用算符優先分析文法,如類似…PQ…..(P,Q為非終結符)這樣形式的產生式,但是對於大部分文法這種分析方法還是十分有用的。
其次,對於本程序中的文法,實質上是算數表達式的計算。用這種文法就是再好不過了,作為從算符文法抽象出來的算符優先文法當然繼承了算符文法的特性。下面就切入正題了,我將詳細介紹一下我對於這個文法的思考出發點和分層分析的方法。
模塊一:構建firstVT()和lastVT()這兩個集合
基於“優先”這兩個字,有效的回避了左遞歸(自上而下文法分析)和二義性的問題。關鍵是如何體現“優先”這兩個字。這就需要firstVT()和lastVT()集合了。
firstVT(E)={a|E→a…..|E→Qa….|firstVT(Q)},從這個定義可以看到,firstVT()主要找到是本產生式中的第一個非終結符和若第一個是非終結符則包含該非終結符的firstVT()集,因為firstVT有可能要求Q的firstVT()集,因此有可能要用遞歸才能求盡所有的firstVT()集。同理,lastVT(E)={a|E→….a|E→…….aQ},可見這兩個關系好像反對稱的影像。說了這么多,還是沒有說到這兩個集合要干什么用。讓我們想象一個句型…aQ…..
在這個句型中我們知道只有等Q的短語規約為Q了,才有可能將…aQ….再次向上規約,因此a的優先級要小於Q產生式的firstVT(Q)集,因為我們可以斷定a必定是比Q中第一個出現的終結符優先級低的,也就是說優先級a<優先級firstVT(Q),至於第二個,第三個終結符。。。我們不敢判定。於是才要費盡心思地構造firstVT()這樣的一個集合。同理,對於lastVT(),讓我們想一下這種情況…..Qa…..,對於這個句型我們知道只有當Q的短語歸約成了Q,我們才敢將….Qa……向上歸約。這樣的話就是說Q的產生式中最后出現的一個終結符的優先級必定是比a的優先級高的,也就是優先級lastVT(Q)>優先級a,同樣的對於倒數第二個,倒數第三個終結符。。。我們不敢判定。說了這么多我想應該能夠理解這兩個集合存在的必要性和決定性了。
因為是程序,我就說一下程序如何實現這兩個集合的求解。
首先是一些數據結構和意義:
char FIRSTVT[20][20];
存儲firstVT()集,第二維代表第幾個產生式,第一維代表集合中的第幾個元素
char LASTVT[20][20];
存儲lastVT()集,第二維代表第幾個產生式,第一維代表集合中的第幾個元素
char INPUT[20][20];
按照一定的形式存儲文法中的所有產生式。
然后是幾個函數:
1. void setFIRSTVT(char X,int T);
這個函數的目的是將求出的終結符X,存放到第T條產生式的左部對應的firstVT集合中。
2. void getFIRSTVT(char X,int S)
S標記產生式的位置,X為將要計算的產生式的左部。這個函數就比較復雜了,它將完成求出一個產生式左部的firstVT的終結符的重要任務,可以說是主控程序了。它的算法是逐個遍歷產生式,對每個產生式求出該產生式的直接a,並且若有E→Qa…還要遞歸的求出Q的firstVT()將其並入firstVT(E)的集合中。
3. 同理void setLASTVT(char X,int T)
4. void getLASTVT(char X,int S)和上面類似。
5. void DisplayFirstVT_LasVT()
這個函數也是主程序開始時要進入的位置。它主要提示用戶輸入文法(產生式組),然后調用上面的函數計算並顯示兩種集合。
下面是void getFIRSTVT(char X,int S)的函數。

1 //找出FIRSTVT集元素
2 void getFIRSTVT(char X,int S) 3 { 4 int i,j=0,k;//j當前數組指針
5 int T=0;//X position
6 int L=0;//X offspring length
7 char C[20]; 8
9 for(i=0;i<20;i++) 10 { 11 if(INPUT[i][0]==X)//找到將要處理的產生式
12 { 13 T=i; //標記產生式的位置
14 break; 15 } 16 } 17 //按照規則從產生式的右部第一個字符開始處理,直至遇上'/n' 18 //每一次都判斷指針j指向的字符若滿足p-->w中w的規定則放進C[] 19 //若遇上‘|’或'\n'則求這段右部對應的firstVT()
20 for(i=4;i<20;i++) 21 { 22 if(INPUT[T][i]=='|'||INPUT[T][i]=='\n') 23 {//剛開始走不到這里
24 L=j;//j指針所指位置為C[]中字符串的長度
25 j=0;//交給L后就清零,以供下一次使用
26 for(k=0;k<L;k++) 27 {//要是數組C[]中的終結符,如小寫字母'a'~'z',加減乘除,乘方,# 28 //則歸入fisrstVT集中 29 //若是Aab...則a成為F()一部分,b被忽略,A也不用求first集???需要求!!!除非A==X 30 //若是QRa...,則不是算符優先文法,故不可能 31 //若是a...則只是填進a
32
33 if((C[k]>=97&&C[k]<=122)||C[k]=='+'||C[k]=='*'||C[k]=='-'||C[k]=='/'||C[k]=='!'||C[k]=='('||C[k]==')'||C[k]=='#'||C[k]=='\?'||C[k]=='=') 34 {//只能用一次,因是算符優先文法,故前兩個中必會至少存在一個終結符
35 setFIRSTVT(C[k],S);//存入FIRSTVT中,S標記產生式的位置
36 break;//跳出循環保證存入的是最左邊的終結符
37 } 38 } 39 if(C[0]>=65&&C[0]<=90&&C[0]!=X) 40 {//若C[0]中為A~Z,並且C[0]不是X(否則將無限循環),則遞歸的進行填充
41 int flag=0,count; 42 for(count=0;count<20;count++) 43 { 44 if(INPUT[count][0]==C[0])//找到將要處理的產生式
45 { 46 flag=1;//若存在,則填充
47 } 48 } 49 if(flag==1) 50 {//遞歸,所用極妙,畫龍點睛
51 getFIRSTVT(C[0],S);//S為子集中的元素填入父集中指明了方向
52 } 53 } 54 } 55 else if(INPUT[T][i]!='|'&&INPUT[T][i]!='\0')//該行沒結束,過濾'\0'
56 { 57 C[j]=INPUT[T][i];//j從0開始
58 j++;//移進
59 } 60 if(INPUT[T][i]=='\n') 61 {//該行結束
62 break; 63 } 64 } 65 }
比如說對於這個文法分析出的集合如下:
模塊二:構建優先符號表
為了體現“優先”的關系,只是構建出了上面的兩種集合是不夠的,由上面分析我們知道了這兩個集合的用處。說白了就是為了構造出算符優先分析表。因為關系無非四類1.等於,2.大於,3.小於,4沒有關系。注意這里的“沒有關系”也是一種關系。
而由第一個階段產生的兩個集合就可以找到所有的大於和小於關系,那就只剩下了等於關系,如果等於關系找到了,剩余的沒有填寫的關系就是沒有關系。等於關系是這樣的:如果存在這樣的句型.....ab....或.......aQb.....則關系(a==b)。這樣的結果也是顯而易見的,因為a和b注定要共同歸約,沒有誰先誰后,因此是等於關系。
好了現在所有關系都搞請了,就可以建表了。
還是首先解釋一下新定義的一個數據結構:
char PriorityTable[20][20];
優先關系表,其中存放着終結符以及終結符對應的關系。
然后是一些函數:
1.int IsVT(char ch)
判斷ch是否為終結符,若是則返回1,否則返回0
2.int SearchTbl(char ch)
搜索終結符ch在符號表中的行列數,用來定位將要填寫的位置。
3.int FL_map(char ch)
這個映射既是查找產生式左部的位置,若存在則返回該產生式的條數,即是第幾個產生式,失敗則返回-1這個出錯標記。
4.void createPriorityTable()
這個是建表的主控程序,用來填寫所有關系與表中。遍歷所有的產生式,填寫所有的關系。這里主要解釋一下程序是如何找到橫縱坐標並且將關系填寫進去的。對於簡單的情況只要拿一個終結符來使用SearchTbl(終結符)就可以找到橫(縱)坐標了,但是因為對於大小關系我們往往要填的是“終結符<firstVT()”或“lastVT()>終結符”這樣的情況,因此勢必要從集合中取出終結符,並用該終結符來映射坐標,即是用到了類似這樣的轉換:
temp_column=FIRSTVT[FL_map(C[2])][count_1];
tbl_column=SearchTbl(temp_column);//將上述結果再次轉換
或者temp_row=LASTVT[FL_map(C[1])][reg];
tbl_row=SearchTbl(temp_row);//map
這樣的映射。知道了這些程序不難讀懂。
5.void DisplayPriorityTable()
這個函數就是顯示優先關系表的內容的。
下面是 void createPriorityTable() 函數的實現過程:

1 void createPriorityTable() 2 {//創建並填寫優先級表 3 //對每個產生式遍歷,求出四種關系1.=,2.<,3.>,4.沒有關系
4 char temp[13]={'+','-','*','/','(',')','v','c','=','?','#','\0'}; 5 int j,L,i; 6 int tbl_row,tbl_column;//表的元素坐標
7 char C[20]; 8 for(int r=1;r<12;r++) 9 { 10 PriorityTable[0][r]=temp[r-1];//初始化行頭,第0行
11 PriorityTable[r][0]=temp[r-1];//初始化第0列
12 } 13 //掃描產生式的右部,如果發現終結符且該終結符周圍有其他字符 14 //若該其他字符為終結符,則這兩者關系為相等 15 //若該其他字符為非終結符,則根據非終結符的firstVT,LastVt填表
16 for(int p=0;p<4;p++) 17 { 18 j=0; 19 for(i=4;i<20;i++) 20 {//注意,此處因為時間有限考慮不是很全面,只是對老師給定的文法進行了周密的部署 21 //在別的文法上可能需要少許加工,不是問題
22 if(INPUT[p][i]=='|'||INPUT[p][i]=='\n') 23 {//剛開始走不到這里
24 L=j;//j指針所指位置為C[]中字符串的長度
25 j=0;//交給L后就清零,以供下一次使用
26 if(L>1)//大於一則處理,否則不關心
27 { //對於清零指令l自動忽略
28 if(IsVT(C[0])&&IsVT(C[1])||(L==3)&&IsVT(C[0])&&IsVT(C[2])&&(FL_map(C[1])!=-1)) 29 {//若為終結符因至少有兩個,若出現兩個終結符則C[0]'=='C[1]||C[2],注意這是此文法的情況 30 //則需要填表,查表找到C[0]的行數,C[1],C[2]的列數進行填表
31 tbl_row=SearchTbl(C[0]);//記錄行數
32 if(IsVT(C[1])) 33 {//列數,若是終結符 v=
34 tbl_column=SearchTbl(C[1]); 35 } 36 if(IsVT(C[2])&&(L==3)) 37 {//列數 (E)
38 tbl_column=SearchTbl(C[2]); 39 } 40 PriorityTable[tbl_row][tbl_column]='=';//填表
41
42 if((L==3)&&IsVT(C[0])&&IsVT(C[1])&&(FL_map(C[2])!=-1)) 43 {//v=E,這種情況
44 int count_1=0; 45 char temp_column; 46 tbl_row=SearchTbl(C[1]);// =<firstVT(E)
47 while(FIRSTVT[FL_map(C[2])][count_1]!='\0') 48 {//填寫小於關系
49 temp_column=FIRSTVT[FL_map(C[2])][count_1];//映射,仔細體會
50 tbl_column=SearchTbl(temp_column);//將上述結果再次轉換
51 PriorityTable[tbl_row][tbl_column]='<';//填寫關系
52 count_1++;//准備填寫下一個
53 } 54 count_1=0;//清零
55 } 56 if((L==3)&&IsVT(C[0])&&IsVT(C[2])&&(FL_map(C[1])!=-1)) 57 {//彌補(E),針對本文法 58 //首先填寫<關系
59 char temp_row,temp_column; 60 int reg=0; 61 tbl_row=SearchTbl(C[0]); 62 while(FIRSTVT[FL_map(C[1])][reg]!='\0') 63 {//填寫小於關系 '('<firstVT(E)
64 temp_column=FIRSTVT[FL_map(C[1])][reg]; 65 tbl_column=SearchTbl(temp_column);//皆是映射
66 PriorityTable[tbl_row][tbl_column]='<'; 67 reg++; 68 } 69 reg=0;//清零
70 tbl_column=SearchTbl(C[2]); 71 while(LASTVT[FL_map(C[1])][reg]!='\0') 72 {//填寫大於關系 lastVT(E)>')'
73 temp_row=LASTVT[FL_map(C[1])][reg]; 74 tbl_row=SearchTbl(temp_row);//map
75 PriorityTable[tbl_row][tbl_column]='>'; 76 reg++; 77 } 78 reg=0;//reset
79 } 80 } 81 else if((FL_map(C[0])!=-1)&&IsVT(C[1])) 82 {//C[0]肯定為非終結符lastVT(C[0])>C[1] 83 //填寫大於關系
84 char temp_row,temp_column; 85 int count=0; 86 tbl_column=SearchTbl(C[1]); 87 while(LASTVT[FL_map(C[0])][count]!='\0') 88 {//填寫大於關系
89 temp_row=LASTVT[FL_map(C[0])][count]; 90 tbl_row=SearchTbl(temp_row);//同上
91 PriorityTable[tbl_row][tbl_column]='>'; 92 count++; 93 } 94 count=0; 95 if(L==3) 96 {//在這種情況下C[2]必為非終結符,求C[2]的firstVT(),填寫小於關系 97 //E+T,E-T,T*F,T/F等情況
98 tbl_row=SearchTbl(C[1]); 99 while(FIRSTVT[FL_map(C[2])][count]!='\0') 100 {//填寫小於關系
101 temp_column=FIRSTVT[FL_map(C[2])][count]; 102 tbl_column=SearchTbl(temp_column);//map
103 PriorityTable[tbl_row][tbl_column]='<'; 104 count++; 105 } 106 count=0; 107 }//end if
108 } 109 } 110 } 111 else if(INPUT[p][i]!='|'&&INPUT[p][i]!='\0')//該行沒結束,過濾'\0'
112 { 113 C[j]=INPUT[p][i];//j從0開始
114 j++;//移進
115 } 116 if(INPUT[p][i]=='\n') 117 {//該行結束
118 break; 119 } 120 } 121 } 122 //補上#的關系
123 for(int m1=1;m1<11;m1++) 124 { 125 PriorityTable[SearchTbl('#')][m1]='<';//這個容易理解,補行
126 } 127 for(int m2=1;m2<11;m2++) 128 { 129 PriorityTable[m2][SearchTbl('#')]='>';//補列
130 } 131 PriorityTable[SearchTbl('#')][SearchTbl('#')]='='; 132 }
比如說對於這個文法分析出來的優先關系表如下:
模塊三:構建詞法分析的程序
做好了上面兩步運算已經累得不行了,下面還要繼續進行分析,那就是詞法分析程序,多虧這個程序在實驗一已經完成過,這里就拿出那里的代碼進行必要的改進和刪除,保留適合本文法要求的部分即可。其實想來無非是用到了識別標識符、識別常數、識別+、-、*、/、?、#、(、)、保留字clear等符號的算法罷了。還是比較好寫的。但考慮到后面的運算,也就是最精彩的部分a=3;b=a+3;這樣的句子,我們不得不在進行詞法分析的時候將這些標識符登記到標識符表中,將句子打碎成二元組存放到符號表中。注意這里的符號表沒有什么其他意義,就是存放將句子解釋成的有序序列罷了。綜上所述,詞法分析這一過程就是識別字符串,若正確則填表,若錯誤,則報錯。兩個任務:填表、報錯。由此也可以看到表格處理和錯誤處理在整個編譯過程中無處不在。
這里新增了一些數據結構和全局變量:
typedef struct
{
char IDentifier[30];//標識符長度
int value;//定義標識符代表的數值
}IDentifierTable;
typedef struct
{
int syn;//種別碼
int value;//數值或者標識符入口指針
}SymbolTbl;
IDentifierTable idTbl[30];//定義全局標識符表
SymbolTbl symbol[100];//定義符號表,記錄輸入的程序片段
下面是這一模塊的具體的函數:
1.void InsertID(char name[],int entry,int value)
功能是將標識符的名字和數值插入到以entry為標識符入口地址的標識符表中。
2.int ScanEntry(char name[])
查找標識符表中是否有空閑位置,若有則返回入口地址,若無則報錯。
3.void Insert_Into_SymbolTbl(int syn,int value,int &pointer)
將句子打散成的所有的二元組(名字和數值)插入到以pointer為符號表入口地址的符號表中。
4.bool IsExist(char id[])
查找表中是否已經存在該標識符
5.int Search_Free_Entity( )
這個函數即是在標識符表中申請一個可用的存儲空間,若有則返回入口地址,否則報錯,申請失敗。
6.bool IsLetter(char letter)
判斷是否為字母
7.bool IsDigit(char digit)
判斷是否為數字
8.void Scanner(char sentence[],char name[],int &syn,int &scan_point,int &value)
掃描子程序,識別正規式。對句子進行掃描,拋出一個個單詞。scan_point為掃描的總指針,name和value用來記錄返回值;sentence即是要分析的句子;syn為種別碼。這個程序是詞法分析的核心,在上一實驗中已詳細介紹過,再次不必贅述。
9.bool Check_Is_Right(char sentence[])
檢查句子是不是#。。。#的形式,因為程序的需要,輸入的句子規定必須是這樣的形式。
10.void LexicalAnalysisCtl()
詞法分析的主控程序,也就是老師上課一再講的那個主控程序。這個程序控制着Scanner(char sentence[],char name[],int &syn,int &scan_point,int &value)產生不同的正規式,並且負責着登記數據和錯誤處理。
11.void Clear_Symbol_Tbl()
這個函數負責着清除符號表中的句子,以便接下來的句子輸入。
12.void Clear_IDentifier_Tbl()
這個函數的功能是清楚標識符表的所有內容。一般會在“清除語句”中使用。
下面是Scanner的程序:

1 void Scanner(char sentence[],char name[],int &syn,int &scan_point,int &value) 2 { 3 char token[30],ch; 4 int p_token=0;//token的指針
5 int digit_value=0;//記錄常數的大小
6 for(int n=0;n<30;n++) 7 { 8 token[n]='\0';//對字符數組清零
9 } 10 ch=sentence[scan_point]; //讀入一個字符
11 if(ch=='#'&&scan_point==0) 12 {//該字符的種別碼已經在主程序中登記了
13 scan_point++;//剛開始的第一個字符一定為‘#’
14 ch=sentence[scan_point];//指針下移,掃描下一字符
15 } 16 if(IsLetter(ch)) //ch是否為字母
17 { 18 while(IsLetter(ch)||IsDigit(ch)) //ch是否為字母或數字
19 { 20 token[p_token++]=ch; 21 scan_point++; 22 ch=sentence[scan_point]; //讀入一個字符
23 } 24 token[p_token]='\0'; 25 syn=9;//代表找到了標識符
26 if(strcmp(token,"clear")==0) 27 {//代表找到了保留字“clear”
28 syn=11; 29 } 30 strcpy(name,token);//帶回標識符
31 } 32 else if(IsDigit(ch)) //ch是否為數字
33 { 34 digit_value=0; 35 while(IsDigit(ch)) 36 { //此處假設只是整數
37 digit_value=digit_value*10+ch-'0'; 38 scan_point++; 39 ch=sentence[scan_point]; //讀入一個字符
40 } 41 value=digit_value;//帶回整數值
42 syn=10; 43 } 44 else
45 { 46 switch(ch) 47 { 48 case'=':syn=1; break; 49 case'?':syn=2; break; 50 case'+':syn=3; break; 51 case'-':syn=4; break; 52 case'*':syn=5; break; 53 case'/':syn=6; break; 54 case'(':syn=7; break; 55 case')':syn=8; break; 56 case'#':syn=12; break; 57 default:printf("輸入句子有誤!\n");exit(0);break; 58 } 59 scan_point++;//保持指針始終指向待判斷的字符
60 ch=sentence[scan_point]; //讀入一個字符
61 } 62 }
下面是主控程序:

1 void LexicalAnalysisCtl() 2 {//主控程序
3 char sentence[100]="\0"; 4 int syn=-1; 5 char name[30]=""; 6 int value; 7 int scan_point=0;//從開始處掃描
8 int id_pointer;//定義標識符表入口指針
9 int sym_pointer=0,entry; 10 do
11 { 12 printf("請輸入句子,以#開始並且以#結束\n"); 13 scanf("%s",sentence); 14 }while(!Check_Is_Right(sentence)); 15 Insert_Into_SymbolTbl(12,-1,sym_pointer); 16 printf("(%d , )\n",12); 17 while(syn!=12) 18 {//直到掃描到第二個‘#’為止
19 Scanner(sentence,name,syn,scan_point,value); 20 switch(syn) 21 {//若是單詞
22 case 9:printf("(%d , %s)\n",syn,name); 23 //登記到表中
24 if(!IsExist(name)) 25 {//不存在則插入 26 //查找可插入位置
27 id_pointer=Search_Free_Entity(); 28 InsertID(name,id_pointer,-1);//-1代表還沒被賦值
29 } 30 //搜索該標識符的入口地址放入value中
31 entry=ScanEntry(name); 32 Insert_Into_SymbolTbl(syn,entry,sym_pointer); 33 break; 34 case 10://常數
35 Insert_Into_SymbolTbl(syn,value,sym_pointer); 36 printf("(%d , %d)\n",syn,value); 37 break; 38 default:printf("(%d , )\n",syn); 39 Insert_Into_SymbolTbl(syn,-1,sym_pointer);//-1代表該處不需要值
40 } 41 } 42 printf("--------------------詞法分析正確!!!-------------------------\n"); 43 //打印出符號表和標識符表
44 printf("標識符的入口地址 標識符 標識符的值(-1代表沒被賦值)\n"); 45 for(int m1=0;m1<30;m1++) 46 {//標識符表
47 if(!(strcmp(idTbl[m1].IDentifier,"")==0)) 48 { 49 printf("\t%d %s %d\n",m1,idTbl[m1].IDentifier,idTbl[m1].value); 50 } 51 } 52 printf("符號表的入口地址 種別碼 具體的值(或標識符入口)\n"); 53 for(int m2=0;m2<sym_pointer;m2++) 54 {//符號表
55 printf("\t%d %d %d\n",m2,symbol[m2].syn ,symbol[m2].value); 56 } 57 printf("---------------------------------------------------------------\n"); 58 }
比如說對於#temp=2+(3*(2+4))#這個句子的詞法分析結果為:
模塊四:核心功能模塊------算符優先分析
前面做了那么多鋪墊就是為了算符優先分析做准備。到了這一步,真的很不容易,因此我們更要對今天的編譯器的處理能力感到驚嘆,的確不是一個人可以完成的,就算一個人可以完成那所用的時間也真的不值。但是對於我們來說,學習一些《編譯原理》上的一些處理問題的辦法和角度,對我們未來的工作和生活絕對是大有裨益的。好了,就說一說這個費了我兩天才完成的核心程序吧。
其實,核心程序並沒有那么難以理解!只要我們舉幾個簡單的例子,模擬一下計算機在處理這個句子的過程,便不難寫出程序。
首先分析我們現在有什么?
1.現在的情況是已經分析出了句子的單詞,並且變成了單詞塊,存放在SymbolTbl symbol[100]中,單詞的形式是(種別碼,常數值)(種別碼,標識符入口地址)、(種別碼,用-1表示無意義)這是三大類。
2.並且分析出了標識符存放在了IDentifierTable idTbl[30]中。標識符的形式是(標識符名字,標識符的值),用-1表示暫未被賦值(這里就湊合一點,時間比較緊,待改進)。
3.分析出了算符優先分析的優先關系表存放在char PriorityTable[20][20]中。
4.已經編碼出了種別碼表,放在char SymbolTbl_Define[15]中,這個是老師要求的次序,便於一致。但又需要一次轉換,以后再說。
好了,我們已經有了這么多數據和方法(函數function),是不是馬上就可以進行下去了呢?!還不行,因為我們迫切需要一個后進先出的存儲結構來保存我們的數據,來完成我們的移進,歸約。那就是——棧。我們需要構造一個這樣的棧:它的每個元素的數據結構都是符號表中元素的數據結構,即為(種別碼,--),--處即為上面分析的三種情況。棧的能力主要有:
template <typename T>
1.T gettop()
獲得棧頂元素
2.int getTopPointer()
得到棧頂指針的數值
3.T traverseStack(int pointer)
定義遍歷整個棧的能力
4. void push(T num)
將元素壓棧的能力
5.T pop()
將元素彈出棧的能力
6.Stack(){top=-1;}
初始化的能力
7.void Clear_Stack()
清空堆棧的能力
數據對象:
Stack<SymbolTbl> Reource_Symbol;//定義堆棧
還有幾個分析將使用的函數:
char SearchSyn(int syn)
根據種別碼返回相應字符,因為我們是在對種別碼進行大小比較,需要先將該種別碼映射成具體的終結符。然后再將該終結符映射成優先關系表中的橫縱坐標。
int Search_Priority_Setxy(char ch)
搜索一個字符在優先符表中的位置,這就是我說的“將該終結符映射成優先關系表中的橫縱坐標。”的映射方法。
void Print_Context(int Stack_Top,int Sym_Pointer)
打印出當前堆棧和符號表的上下文
void MainProc_Analysis()
主分析函數,整個算法的核心。
好了,有了這些東西,我們的分析總算可以一馬平川了。
1.首先將符號表的第一個元素#,對應的(種別碼,-1)壓棧,即
SymbolTbl temp_sym;
temp_sym.syn=12;
temp_sym.value=-1;//-1代表這個地方不需要
Reource_Symbol.push(temp_sym);//將#壓棧
2.對於堆棧定義兩個指針,一個指針pStackTop始終指向棧頂,在棧被修改(push,pop)之后都要修改。另一個堆棧指針是pScanStack,負責對整個堆棧的掃描,不斷地查找可歸約串的結束位置。 對於符號表,定義Sym_scan_pointer指針,從一開始,對符號表進行掃描。
3.因為現在不僅是語法分析,還包含了語義分析,我們要定義好語義子程序,比如說“清除語句”,#clear#,我們遇到這個東西時,就要1.清空符號表和標識符表;2.清除屏幕(我自己理解的,清除應該就是這些吧。。。) 因此,我們在進行其他分析之前,先把這個清除語句搞定。
if(sym_length>=3&&(symbol[1].syn==11))
{//清除語句,清除屏幕並且清空標號表中的值
Clear_IDentifier_Tbl();
Clear_Symbol_Tbl();
system("cls");
goto end;
}
4.掃除了這些障礙,我們總算可以進行真正的分析了。
首先棧掃描指針和棧頂指針同時指向棧頂開始分析:
下面進行永真循環:
*******************************************************************
{
查看棧掃描指針pScanStack所指的元素和符號表指針所指元素的關系。
4.1若為小於:
則將符號表指針所指元素壓棧,修改棧的兩個指針都指向棧頂。符號表指針下移。
4.2若為等於:
此時要分兩種情況:
@1.若是棧掃描指針pScanStack所指的元素和符號表指針所指元素都是#,則查看棧中是否只有兩個元素且棧頂元素是否為N,若為真,則說明歸約成功,輸出棧頂的值,然后結束分析。否則,則報錯。
@2.若不是上面的情況,則按照小於關系處理。
4.3若為大於:
這個時候,就是激動人心的時候了,因為要進行歸約了。
首先對齊pStackTop,pScanStack這兩個指針,雖然這個時候肯定是對齊的(自己想),然后進入小循環 while(Prior_Relation=='>'),小循環的內容是:
****************小循環開始************************
判斷現在棧掃描指針所指元素的種別碼,該種別碼所對應元素即是大於符號表中指針所指的元素。
4.3.1、若該元素為標識符:語義分析:判斷標識符是否已定義;彈出棧頂元素,將N(13,標識符的值)壓入棧中,這里取了一次巧,即是讓N來承載歸約的結果,更加方便。重新修改棧頂指針,棧掃描指針,將棧掃描指針指向從棧頂數第一個終結符的位置,即下移。
pScanStack=Reource_Symbol.getTopPointer()-1;
重新定位行列指針(列不變),修改關系。
row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn));
Prior_Relation=PriorityTable[row_prior][column_prior];
4.3.2、若該元素為常量,則分析的過程和標識符極其相似,唯一不同的是,N(13,值)中的‘值’,一個是從標識符表中查到的,另一個是直接就有的。
4.3.3、若該元素為‘=’,這時根據文法的要求,出現等號就應該滿足“v=N”這種情況。首先判斷是否滿足,若不滿足則報錯,若滿足,則將這三個元素歸約為一個元素N(13,舊N.value),並且要反填符號表,將該變量的值賦為舊N.value。這一步語義分析十分重要,直接關系着以后引用這個變量時是否能夠找到它的值。
idTbl[Reource_Symbol.traverseStack(pScanStack-1).value].value=Reource_Symbol.gettop().value;//此時棧頂必為E
然后就是修改棧的兩個指針,重新定位行列(列不變),修改關系。
4.3.4、若該元素為+、-、*、/,則這四個的處理方式一樣。首先判斷棧頂是否滿足N op N,op={+|-|*|/},若滿足則歸約,否則認為是錯誤的,進行報錯處理。規約之后,同樣的將N op N歸約為N,並且將 “新N.value=(N1.value op N1.value)” ------語義分析
然后就是修改棧的兩個指針,重新定位行列(列不變),修改關系。
4.3.5、若該元素為‘)’,這個時候棧頂一定為(N),若不是,則句子出錯,進行錯誤處理,又因為’(‘是大於任何終結符的,因此(N)就構成了“魚眼睛”,
“< = >”,所以需要歸約將(N)歸約為N,做相應的語義分析子程序:
N.value=(N1.value),然后就是修改棧的兩個指針,重新定位行列(列不變),修改關系。 因為’(‘小於任何終結符,因此不會出現在這里。
然后就是修改棧的兩個指針,重新定位行列(列不變),修改關系。
4.3.6、若該元素為‘?’根據文法,出現這個東西,就是要打印N?中N的值了,因此先查看棧頂是否滿足N?若滿足,則歸約,並作相應的語義動作,打印。
然后就是修改棧的兩個指針,重新定位行列(列不變),修改關系。
滿足大於關系的就這些終結符,我已一一分析了它們的語法分析和語義的動作。若該元素不是上面的終結符,則認為句子錯誤。否則將進行 while(Prior_Relation=='>'),的判斷,若滿足繼續循環,否則,進入永真大循環中。
****************小循環結束*********************
}//永真循環結尾
*******************************************************************
上面就是整個算符優先算法的精髓了。
比如說對於上文的#temp=2+(3*(2+4))#分析的過程和結果為:
經過這么長時間的分析又浪費了一個晚上的時間,但覺得還是一氣呵成,並且又理了一遍思路,還是值得的。
通過上面的敘述,整個程序的結構呼之欲出:
五.總控模塊
最后的main()函數直接調用各個模塊就可以了。代碼如下:

int main() { char ch;//是否繼續的標記 //計算並顯示firstVT()和LastVT()
DisplayFirstVT_LasVT(); //創建算符優先關系表
createPriorityTable(); //打印算符優先關系表
DisplayPriorityTable(); //cout<<"請輸入語句的個數"<<endl;
while(1) { //首先要清空符號表,這樣才能進行下一輪分析
Clear_Symbol_Tbl(); //詞法分析,登記符號表
LexicalAnalysisCtl(); //語法語義分析,產生運行結果
MainProc_Analysis(); cout<<"continue? y or n"<<endl; cin>>ch; if(!(ch=='y')&&!(ch=='Y')) { cout<<"the procedure is end"<<endl; exit(0); } } return 0; }
好了,這次的實驗分析就到這里了。
2.3.程序架構設計
根據上面的思路,我采用了分文件的形式,這樣思路比較清晰,程序比較簡潔。下面的四個頭文件中分別存放的是四個模塊的代碼。
#include "firstVT_lastVT.h"//計算firstVT()和LastVT()的程序
#include "GetPriorityTable.h"//創建算符優先關系表的程序
#include "Simple_Lexical_Analysis.h"//詞法分析的程序
#include "MainProc_Analysis.h" //主分析程序,用來進行語法和語義分析
再加上最后的總控程序,整個程序的架構就是這個樣子了。
2.4.測試
此處使用老師給的范例:
演示示例:
a=5
b=a+10
b?
b+a*a?
a=a+b
這里試驗一下“清除語句”的作用:
比若說輸入#a=3#:
這里歸約的結果為3,即a=3,此時用清除語句,將清屏,並清除標識符表。#clear#
回車之后:
此時輸入#b=2#,可以看到表示表中只有b,沒有a
則說明a被清除。
2.5、感想
這次的實驗花費了我不少時間,但也從中學到了很多,從最初的第一個模塊一路走來,也遇到許多挫折,特別是明明邏輯都對卻找不到錯誤的原因的時候,多虧了調試工具。 編寫這個程序一部分來自於學習的課程要求,另一部分也來自於自己對編程的喜愛,或許正是這種感覺一直支持我到現在吧。代碼雖然寫完了,對於老師給的文法絕對是沒問題的,可是畢竟也有它的局限性。即便如此,編譯原理的處理問題的思想,思考問題的角度,都讓我大受啟發。這個程序其實是很全面的,它從詞法分析-->語法分析-->語義分析---->問題求解,基本上走完了編譯器的大部分生命周期。對於我們理解編譯的過程和具體的實現都是很有幫助的。當然,編譯原理博大精深,比如說對於數組的處理、中間代碼的生成等很多方面,都值得我們認真揣摩。
2.6、源代碼(所有)

1 源代碼 2 模塊一: 3 /****************#include"firstVT_lastVT.h"************************/
4
5 //程序功能大意說明 6 //該子程序主要是求算符優先文法中的firstVT()和lastVT() 7 //因為求firstVT()和lastVT(),可能造成的遞歸性,我們需要慎重對待。 8 //直至求完所有集合的元素為止 9 //這里要注意遞歸的運用和FirstVT(),LastVT()的定義 10 //firstVT(E)為a...或Qa....中的終結符a,以及firstVT(Q)中的所有元素。 11 //lastVT(E)為...a或....aQ中的終結符a,以及lastVT(Q)中的所有元素。 12
13 //引用外部變量
14 extern char FIRSTVT[20][20]; 15 extern char LASTVT[20][20]; 16 extern char PriorityTable[20][20]; 17 extern char INPUT[20][20]; 18
19 //填寫firstVT集合
20 void setFIRSTVT(char X,int T) 21 {//X為終結符,T標記產生式id
22 int i,j; 23 for(i=0;i<20;i++) 24 { 25 if(i==T) 26 { 27 for(j=0;j<20;j++) 28 {//掃描判斷是否加入
29 if(X==FIRSTVT[i][j]) 30 {//若集合中已出現,則不加
31 return; 32 } 33 else if(FIRSTVT[i][j]=='\0') 34 { 35 FIRSTVT[i][j]=X;//填入數組最后
36 break; 37 } 38 } 39 break; 40 } 41 } 42 } 43 //找出FIRSTVT集元素
44 void getFIRSTVT(char X,int S) 45 { 46 int i,j=0,k;//j當前數組指針
47 int T=0;//X position
48 int L=0;//X offspring length
49 char C[20]; 50
51 for(i=0;i<20;i++) 52 { 53 if(INPUT[i][0]==X)//找到將要處理的產生式
54 { 55 T=i; //標記產生式的位置
56 break; 57 } 58 } 59 //按照規則從產生式的右部第一個字符開始處理,直至遇上'/n' 60 //每一次都判斷指針j指向的字符若滿足p-->w中w的規定則放進C[] 61 //若遇上‘|’或'\n'則求這段右部對應的firstVT()
62 for(i=4;i<20;i++) 63 { 64 if(INPUT[T][i]=='|'||INPUT[T][i]=='\n') 65 {//剛開始走不到這里
66 L=j;//j指針所指位置為C[]中字符串的長度
67 j=0;//交給L后就清零,以供下一次使用
68 for(k=0;k<L;k++) 69 {//要是數組C[]中的終結符,如小寫字母'a'~'z',加減乘除,乘方,# 70 //則歸入fisrstVT集中 71 //若是Aab...則a成為F()一部分,b被忽略,A也不用求first集???需要求!!!除非A==X 72 //若是QRa...,則不是算符優先文法,故不可能 73 //若是a...則只是填進a
74
75 if((C[k]>=97&&C[k]<=122)||C[k]=='+'||C[k]=='*'||C[k]=='-'||C[k]=='/'||C[k]=='!'||C[k]=='('||C[k]==')'||C[k]=='#'||C[k]=='\?'||C[k]=='=') 76 {//只能用一次,因是算符優先文法,故前兩個中必會至少存在一個終結符
77 setFIRSTVT(C[k],S);//存入FIRSTVT中,S標記產生式的位置
78 break;//跳出循環保證存入的是最左邊的終結符
79 } 80 } 81 if(C[0]>=65&&C[0]<=90&&C[0]!=X) 82 {//若C[0]中為A~Z,並且C[0]不是X(否則將無限循環),則遞歸的進行填充
83 int flag=0,count; 84 for(count=0;count<20;count++) 85 { 86 if(INPUT[count][0]==C[0])//找到將要處理的產生式
87 { 88 flag=1;//若存在,則填充
89 } 90 } 91 if(flag==1) 92 {//遞歸,所用極妙,畫龍點睛
93 getFIRSTVT(C[0],S);//S為子集中的元素填入父集中指明了方向
94 } 95 } 96 } 97 else if(INPUT[T][i]!='|'&&INPUT[T][i]!='\0')//該行沒結束,過濾'\0'
98 { 99 C[j]=INPUT[T][i];//j從0開始
100 j++;//移進
101 } 102 if(INPUT[T][i]=='\n') 103 {//該行結束
104 break; 105 } 106 } 107 } 108
109 //存儲LASTVT集
110 void setLASTVT(char X,int T) 111 {//X為終結符,T標記產生式id
112 int i,j; 113 for(i=0;i<20;i++) 114 { 115 if(i==T) 116 { 117 for(j=0;j<20;j++) 118 { 119 if(X==LASTVT[i][j]) 120 {//若集合中已出現,則不加
121 break; 122 } 123 else if(LASTVT[i][j]=='\0') 124 { 125 LASTVT[i][j]=X;//填入數組最后
126 return; 127 } 128 } 129 break; 130 } 131 } 132 } 133
134 //找出LASTVT集元素
135 void getLASTVT(char X,int S) 136 { 137 int i,j=0,k;//j當前數組指針
138 int T=0;//X position
139 int L=0;//X offspring length
140 char C[20]; 141
142 for(i=0;i<20;i++) 143 { 144 if(INPUT[i][0]==X)//找到將要處理的產生式
145 { 146 T=i; //標記產生式的位置
147 break; 148 } 149 } 150 //按照規則從產生式的右部第一個字符開始處理,直至遇上'/n' 151 //每一次都判斷指針j指向的字符若滿足p-->w中w的規定則放進C[] 152 //若遇上‘|’或'\n'則求這段右部對應的LASTVT()
153 for(i=4;i<20;i++) 154 { 155 if(INPUT[T][i]=='|'||INPUT[T][i]=='\n') 156 {//剛開始走不到這里
157 L=j;//j指針所指位置為C[]中字符串的長度
158 j=0;//交給L后就清零,以供下一次使用
159 for(k=L-1;k>=0;k--) 160 {//要是數組C[]中的終結符,如小寫字母'a'~'z',加減乘除,乘方,# 161 //則歸入LASTVT集中 162 //若是Aab...則a成為F()一部分,b被忽略,A也不用求first集???需要求!!!除非A==X 163 //若是QRa...,則不是算符優先文法,故不可能 164 //若是a...則只是填進a
165
166 if((C[k]>=97&&C[k]<=122)||C[k]=='+'||C[k]=='*'||C[k]=='-'||C[k]=='/'||C[k]=='!'||C[k]=='('||C[k]==')'||C[k]=='#'||C[k]=='\?'||C[k]=='=') 167 {//只能用一次
168 setLASTVT(C[k],S);//存入LASTVT中,S標記產生式的位置
169 break;//跳出循環保證存入的是最左邊的終結符
170 } 171 } 172 if(C[L-1]>=65&&C[L-1]<=90&&C[L-1]!=X) 173 {//若C[0]中為A~Z,並且C[]中沒有終結符,則遞歸的進行填充
174 int flag=0,count; 175 for(count=0;count<20;count++) 176 { 177 if(INPUT[count][0]==C[L-1])//找到將要處理的產生式
178 { 179 flag=1; 180 } 181 } 182 if(flag==1) 183 {//同firstVT()
184 getLASTVT(C[L-1],S);//S為子集中的元素填入父集中指明了方向
185 } 186 } 187 } 188 else if(INPUT[T][i]!='|'&&INPUT[T][i]!='\0')//該行沒結束,過濾'\0'
189 { 190 C[j]=INPUT[T][i];//j從0開始
191 j++;//移進
192 } 193 if(INPUT[T][i]=='\n') 194 {//該行結束
195 break; 196 } 197 } 198 } 199
200
201
202 void DisplayFirstVT_LasVT() 203 { 204 int i,j; 205 printf("\t*-------------------------------------------------------*\n"); 206 printf("\t|\t歡迎使用算符優先文法分析器...... |\n"); 207 printf("\t|\t因時間有限,適用范圍可能有限! |\n"); 208 printf("\t|\t請輸入算符優先文法,按兩次回車代表輸入完成: |\n"); 209 printf("\t|\t版權所有:軟件工程2班,朱彥榮,20132184 |\n"); 210 printf("\t*-------------------------------------------------------*\n"); 211 for(i=0;i<20;i++) 212 {//獲得產生式放入input[][]中
213 for(j=0;j<20;j++) 214 { 215 scanf("%c",&INPUT[i][j]); 216 if(INPUT[i][j]=='\n') 217 break;//每行接收一個產生式
218 } 219 if(INPUT[i][0]=='\n') 220 break;//遇到又一次回車,結束輸入
221 } 222 //保存FIRSTVT集
223 for(i=0;INPUT[i][0]!='\n';i++) 224 {//對所有的產生式
225 getFIRSTVT(INPUT[i][0],i);//第i+1個產生式,求fistvt(),核心算法
226 } 227 printf("FIRSTVT SET:\n"); 228 for(i=0;INPUT[i][0]!='\n';i++) 229 {//對所有產生式,進行輸出fistvt集,最大處理能力20個產生式
230 printf("FIRSTVT("); 231 printf("%c",INPUT[i][0]); 232 printf(")={"); 233 for(j=0;FIRSTVT[i][j]!='\0';j++) 234 { 235 printf("%c",FIRSTVT[i][j]); 236 if(FIRSTVT[i][j+1]!='\0') 237 { 238 printf(",");//添加逗號
239 } 240 } 241 printf("}\n"); 242 } 243
244 printf("\n"); 245 for(i=0;INPUT[i][0]!='\n';i++) 246 {//對所有的產生式
247 getLASTVT(INPUT[i][0],i);//第i+1個產生式,求lastvt(),核心算法
248 } 249 for(i=0;INPUT[i][0]!='\n';i++) 250 { 251 printf("LASTVT("); 252 printf("%c",INPUT[i][0]); 253 printf(")={"); 254 for(j=0;LASTVT[i][j]!='\0';j++) 255 {//逐次輸出
256 printf("%c",LASTVT[i][j]); 257 if(LASTVT[i][j+1]!='\0') 258 printf(","); 259 } 260 printf("}\n"); 261 } 262 printf("\n"); 263 } 264 /*******************end #include "firstVT_lastVT.h"************************/
265
266 模塊二: 267 /**************#include "GetPriorityTable.h"**********************/
268 //此分程序主要是計算優先關系表 269 //優先分析表是算符優先文法的關鍵,因此應該慎重對待 270 //如何建表,建什么類型的表,表的使用是不是方便都是我們要考慮的情況
271
272 extern char FIRSTVT[20][20]; 273 extern char LASTVT[20][20]; 274 extern char PriorityTable[20][20]; 275 extern char INPUT[20][20]; 276
277 int IsVT(char ch) 278 {//判斷是否是終結符
279 if((ch>=97&&ch<=122)||ch=='+'||ch=='*'||ch=='-'||ch=='/'||ch=='!'||ch=='('||ch==')'||ch=='#'||ch=='\?'||ch=='=') 280 { 281 return 1;//為終結符
282 } 283 return 0;//不是終結符
284 } 285
286 int SearchTbl(char ch) 287 {//返回符號所在的行列
288 int index=-1; 289 //該排列即為優先關系表中元素的排列情況 290 //行和列的排列相同便於查找和引用 291 //因此即可以查行又可以查列
292 char temp[13]={'+','-','*','/','(',')','v','c','=','?','#','\0'}; 293 for(int i=0;i<11;i++) 294 { 295 if(ch==temp[i]) 296 { 297 index=i+1;//之所以是i+1,因為該順序從一開始
298 break; 299 } 300 } 301 return index;//返回所查找的橫(縱)坐標
302 } 303
304 int FL_map(char ch) 305 {//這個映射既是查找產生式左部的位置
306 switch(ch) 307 { 308 case 'S': return 0;break; 309 case 'E': return 1;break; 310 case 'T': return 2;break; 311 case 'F': return 3;break; 312 default: return -1;break;//查找失敗
313 } 314 } 315
316 void createPriorityTable() 317 {//創建並填寫優先級表 318 //對每個產生式遍歷,求出四種關系1.=,2.<,3.>,4.沒有關系
319 char temp[13]={'+','-','*','/','(',')','v','c','=','?','#','\0'}; 320 int j,L,i; 321 int tbl_row,tbl_column;//表的元素坐標
322 char C[20]; 323 for(int r=1;r<12;r++) 324 { 325 PriorityTable[0][r]=temp[r-1];//初始化行頭,第0行
326 PriorityTable[r][0]=temp[r-1];//初始化第0列
327 } 328 //掃描產生式的右部,如果發現終結符且該終結符周圍有其他字符 329 //若該其他字符為終結符,則這兩者關系為相等 330 //若該其他字符為非終結符,則根據非終結符的firstVT,LastVt填表
331 for(int p=0;p<4;p++) 332 { 333 j=0; 334 for(i=4;i<20;i++) 335 {//注意,此處因為時間有限考慮不是很全面,只是對老師給定的文法進行了周密的部署 336 //在別的文法上可能需要少許加工,不是問題
337 if(INPUT[p][i]=='|'||INPUT[p][i]=='\n') 338 {//剛開始走不到這里
339 L=j;//j指針所指位置為C[]中字符串的長度
340 j=0;//交給L后就清零,以供下一次使用
341 if(L>1)//大於一則處理,否則不關心
342 { //對於清零指令l自動忽略
343 if(IsVT(C[0])&&IsVT(C[1])||(L==3)&&IsVT(C[0])&&IsVT(C[2])&&(FL_map(C[1])!=-1)) 344 {//若為終結符因至少有兩個,若出現兩個終結符則C[0]'=='C[1]||C[2],注意這是此文法的情況 345 //則需要填表,查表找到C[0]的行數,C[1],C[2]的列數進行填表
346 tbl_row=SearchTbl(C[0]);//記錄行數
347 if(IsVT(C[1])) 348 {//列數,若是終結符 v=
349 tbl_column=SearchTbl(C[1]); 350 } 351 if(IsVT(C[2])&&(L==3)) 352 {//列數 (E)
353 tbl_column=SearchTbl(C[2]); 354 } 355 PriorityTable[tbl_row][tbl_column]='=';//填表
356
357 if((L==3)&&IsVT(C[0])&&IsVT(C[1])&&(FL_map(C[2])!=-1)) 358 {//v=E,這種情況
359 int count_1=0; 360 char temp_column; 361 tbl_row=SearchTbl(C[1]);// =<firstVT(E)
362 while(FIRSTVT[FL_map(C[2])][count_1]!='\0') 363 {//填寫小於關系
364 temp_column=FIRSTVT[FL_map(C[2])][count_1];//映射,仔細體會
365 tbl_column=SearchTbl(temp_column);//將上述結果再次轉換
366 PriorityTable[tbl_row][tbl_column]='<';//填寫關系
367 count_1++;//准備填寫下一個
368 } 369 count_1=0;//清零
370 } 371 if((L==3)&&IsVT(C[0])&&IsVT(C[2])&&(FL_map(C[1])!=-1)) 372 {//彌補(E),針對本文法 373 //首先填寫<關系
374 char temp_row,temp_column; 375 int reg=0; 376 tbl_row=SearchTbl(C[0]); 377 while(FIRSTVT[FL_map(C[1])][reg]!='\0') 378 {//填寫小於關系 '('<firstVT(E)
379 temp_column=FIRSTVT[FL_map(C[1])][reg]; 380 tbl_column=SearchTbl(temp_column);//皆是映射
381 PriorityTable[tbl_row][tbl_column]='<'; 382 reg++; 383 } 384 reg=0;//清零
385 tbl_column=SearchTbl(C[2]); 386 while(LASTVT[FL_map(C[1])][reg]!='\0') 387 {//填寫大於關系 lastVT(E)>')'
388 temp_row=LASTVT[FL_map(C[1])][reg]; 389 tbl_row=SearchTbl(temp_row);//map
390 PriorityTable[tbl_row][tbl_column]='>'; 391 reg++; 392 } 393 reg=0;//reset
394 } 395 } 396 else if((FL_map(C[0])!=-1)&&IsVT(C[1])) 397 {//C[0]肯定為非終結符lastVT(C[0])>C[1] 398 //填寫大於關系
399 char temp_row,temp_column; 400 int count=0; 401 tbl_column=SearchTbl(C[1]); 402 while(LASTVT[FL_map(C[0])][count]!='\0') 403 {//填寫大於關系
404 temp_row=LASTVT[FL_map(C[0])][count]; 405 tbl_row=SearchTbl(temp_row);//同上
406 PriorityTable[tbl_row][tbl_column]='>'; 407 count++; 408 } 409 count=0; 410 if(L==3) 411 {//在這種情況下C[2]必為非終結符,求C[2]的firstVT(),填寫小於關系 412 //E+T,E-T,T*F,T/F等情況
413 tbl_row=SearchTbl(C[1]); 414 while(FIRSTVT[FL_map(C[2])][count]!='\0') 415 {//填寫小於關系
416 temp_column=FIRSTVT[FL_map(C[2])][count]; 417 tbl_column=SearchTbl(temp_column);//map
418 PriorityTable[tbl_row][tbl_column]='<'; 419 count++; 420 } 421 count=0; 422 }//end if
423 } 424 } 425 } 426 else if(INPUT[p][i]!='|'&&INPUT[p][i]!='\0')//該行沒結束,過濾'\0'
427 { 428 C[j]=INPUT[p][i];//j從0開始
429 j++;//移進
430 } 431 if(INPUT[p][i]=='\n') 432 {//該行結束
433 break; 434 } 435 } 436 } 437 //補上#的關系
438 for(int m1=1;m1<11;m1++) 439 { 440 PriorityTable[SearchTbl('#')][m1]='<';//這個容易理解,補行
441 } 442 for(int m2=1;m2<11;m2++) 443 { 444 PriorityTable[m2][SearchTbl('#')]='>';//補列
445 } 446 PriorityTable[SearchTbl('#')][SearchTbl('#')]='='; 447 } 448
449 void DisplayPriorityTable() 450 {//顯示優先關系表查看是否正確
451 int i,j; 452 printf("構造的算符優先關系表如下:\n"); 453 for(i=0;i<12;i++) 454 { 455 for(j=0;j<12;j++) 456 { 457 printf("%c ",PriorityTable[i][j]); 458 } 459 printf("\n"); 460 } 461 } 462 /*****************#include "GetPriorityTable.h"*******************/
463
464
465
466 模塊三: 467
468 /***************#include"Simple_Lexical_Analysis.h"*******************/
469 #include "stdlib.h"
470 #include "string.h"
471 #include "math.h"
472 #include "iostream"
473 using namespace std; 474
475 typedef struct
476 { 477 char IDentifier[30];//標識符長度
478 int value;//定義標識符代表的數值
479 }IDentifierTable; 480
481 typedef struct
482 { 483 int syn;//種別碼
484 int value;//數值或者標識符入口指針
485 }SymbolTbl; 486 extern char FIRSTVT[20][20]; 487 extern char LASTVT[20][20]; 488 extern char PriorityTable[20][20]; 489 extern char INPUT[20][20]; 490 extern IDentifierTable idTbl[30]; 491 extern SymbolTbl symbol[100];//定義符號表
492 /*單詞種別碼設計: 493 = 1 494 ? 2 495 + 3 496 - 4 497 * 5 498 / 6 499 ( 7 500 ) 8 501 v 9 502 c 10 503 clear 11 504 # 12 505 N 13 506 */
507 //此處簡單起見,就只考慮標識符,運算符,數字 508 //定義一個結構存儲(種別碼、單詞的入口地址)或者(種別碼,常數值)或者(種別碼)
509 void InsertID(char name[],int entry,int value) 510 {//插入到標識符表中
511 strcpy(idTbl[entry].IDentifier,name);//插入名字
512 idTbl[entry].value=value;//插入數值
513 } 514
515 int ScanEntry(char name[]) 516 {//查找符號表中是否有空閑位置
517 for(int i=0;i<30;i++) 518 { 519 if(strcmp(idTbl[i].IDentifier,name)==0) 520 { 521 return i;//返回入口地址
522 } 523 } 524 return -1;//失敗返回失敗碼
525 } 526
527
528 void Insert_Into_SymbolTbl(int syn,int value,int &pointer) 529 {//插入到符號表中
530 symbol[pointer].syn = syn; 531 symbol[pointer].value = value; 532 pointer++; 533 } 534
535 bool IsExist(char id[]) 536 {//查找表中是否已經存在該標識符
537 int i=0; 538 for(i=0;i<30;i++) 539 { 540 if(strcmp(idTbl[i].IDentifier,id)==0) 541 { 542 return true; 543 } 544 } 545 return false; 546 } 547
548
549 int Search_Free_Entity( ) 550 {//查找表中是否已經存在該標識符
551 int i=0; 552 for(i=0;i<30;i++) 553 { 554 if(strcmp(idTbl[i].IDentifier,"")==0) 555 { 556 return i;//返回空閑入口
557 } 558 } 559 return -1;//查找失敗
560 } 561
562 /*********************判斷是否為字母********************/
563 bool IsLetter(char letter) 564 {//注意C語言允許下划線也為標識符的一部分可以放在首部或其他地方
565 if (letter >= 'a'&&letter <= 'z' || letter >= 'A'&&letter <= 'Z' || letter=='_') 566 { 567 return true; 568 } 569 else
570 { 571 return false; 572 } 573 } 574 /*********************判斷是否為字母********************/
575
576
577 /*****************判斷是否為數字************************/
578 bool IsDigit(char digit) 579 { 580 if (digit >= '0'&&digit <= '9') 581 { 582 return true; 583 } 584 else
585 { 586 return false; 587 } 588 } 589 /*****************判斷是否為數字************************/
590
591 void Scanner(char sentence[],char name[],int &syn,int &scan_point,int &value) 592 { 593 char token[30],ch; 594 int p_token=0;//token的指針
595 int digit_value=0;//記錄常數的大小
596 for(int n=0;n<30;n++) 597 { 598 token[n]='\0';//對字符數組清零
599 } 600 ch=sentence[scan_point]; //讀入一個字符
601 if(ch=='#'&&scan_point==0) 602 {//該字符的種別碼已經在主程序中登記了
603 scan_point++;//剛開始的第一個字符一定為‘#’
604 ch=sentence[scan_point];//指針下移,掃描下一字符
605 } 606 if(IsLetter(ch)) //ch是否為字母
607 { 608 while(IsLetter(ch)||IsDigit(ch)) //ch是否為字母或數字
609 { 610 token[p_token++]=ch; 611 scan_point++; 612 ch=sentence[scan_point]; //讀入一個字符
613 } 614 token[p_token]='\0'; 615 syn=9;//代表找到了標識符
616 if(strcmp(token,"clear")==0) 617 {//代表找到了保留字“clear”
618 syn=11; 619 } 620 strcpy(name,token);//帶回標識符
621 } 622 else if(IsDigit(ch)) //ch是否為數字
623 { 624 digit_value=0; 625 while(IsDigit(ch)) 626 { //此處假設只是整數
627 digit_value=digit_value*10+ch-'0'; 628 scan_point++; 629 ch=sentence[scan_point]; //讀入一個字符
630 } 631 value=digit_value;//帶回整數值
632 syn=10; 633 } 634 else
635 { 636 switch(ch) 637 { 638 case'=':syn=1; break; 639 case'?':syn=2; break; 640 case'+':syn=3; break; 641 case'-':syn=4; break; 642 case'*':syn=5; break; 643 case'/':syn=6; break; 644 case'(':syn=7; break; 645 case')':syn=8; break; 646 case'#':syn=12; break; 647 default:printf("輸入句子有誤!\n");exit(0);break; 648 } 649 scan_point++;//保持指針始終指向待判斷的字符
650 ch=sentence[scan_point]; //讀入一個字符
651 } 652 } 653 bool Check_Is_Right(char sentence[]) 654 {//檢查句子是不是#。。。#的形式
655 int len=strlen(sentence)-1; 656
657 if(sentence[0]=='#'&&sentence[len]=='#') 658 {//&&sentence[strlen[sentence]-1]=='#'
659 return true; 660 } 661 return false; 662 } 663 void LexicalAnalysisCtl() 664 {//主控程序
665 char sentence[100]="\0"; 666 int syn=-1; 667 char name[30]=""; 668 int value; 669 int scan_point=0;//從開始處掃描
670 int id_pointer;//定義標識符表入口指針
671 int sym_pointer=0,entry; 672 do
673 { 674 printf("請輸入句子,以#開始並且以#結束\n"); 675 scanf("%s",sentence); 676 }while(!Check_Is_Right(sentence)); 677 Insert_Into_SymbolTbl(12,-1,sym_pointer); 678 printf("(%d , )\n",12); 679 while(syn!=12) 680 {//直到掃描到第二個‘#’為止
681 Scanner(sentence,name,syn,scan_point,value); 682 switch(syn) 683 {//若是單詞
684 case 9:printf("(%d , %s)\n",syn,name); 685 //登記到表中
686 if(!IsExist(name)) 687 {//不存在則插入 688 //查找可插入位置
689 id_pointer=Search_Free_Entity(); 690 InsertID(name,id_pointer,-1);//-1代表還沒被賦值
691 } 692 //搜索該標識符的入口地址放入value中
693 entry=ScanEntry(name); 694 Insert_Into_SymbolTbl(syn,entry,sym_pointer); 695 break; 696 case 10://常數
697 Insert_Into_SymbolTbl(syn,value,sym_pointer); 698 printf("(%d , %d)\n",syn,value); 699 break; 700 default:printf("(%d , )\n",syn); 701 Insert_Into_SymbolTbl(syn,-1,sym_pointer);//-1代表該處不需要值
702 } 703 } 704 printf("--------------------詞法分析正確!!!-------------------------\n"); 705 //打印出符號表和標識符表
706 printf("標識符的入口地址 標識符 標識符的值(-1代表沒被賦值)\n"); 707 for(int m1=0;m1<30;m1++) 708 {//標識符表
709 if(!(strcmp(idTbl[m1].IDentifier,"")==0)) 710 { 711 printf("\t%d %s %d\n",m1,idTbl[m1].IDentifier,idTbl[m1].value); 712 } 713 } 714 printf("符號表的入口地址 種別碼 具體的值(或標識符入口)\n"); 715 for(int m2=0;m2<sym_pointer;m2++) 716 {//符號表
717 printf("\t%d %d %d\n",m2,symbol[m2].syn ,symbol[m2].value); 718 } 719 printf("---------------------------------------------------------------\n"); 720 } 721
722
723 void Clear_Symbol_Tbl() 724 { 725 //將符號表的syn全部設置為0
726 for(int i=0;i<100;i++) 727 { 728 symbol[i].syn=0;//代表沒定義
729 symbol[i].value=-1;//指定為-1
730 } 731 } 732
733
734 void Clear_IDentifier_Tbl() 735 {//清空標識符表
736 for(int i=0;i<30;i++) 737 { 738 strcpy(idTbl[i].IDentifier,""); 739 idTbl[i].value=-1; 740 } 741 } 742 /*************#include"Simple_Lexical_Analysis.h"****************/
743
744
745 模塊四: 746
747 /***********#include "MainProc_Analysis.h"********************/
748
749 #include"iostream"
750 using namespace std; 751
752 extern char FIRSTVT[20][20]; 753 extern char LASTVT[20][20]; 754 extern char PriorityTable[20][20]; 755 extern char INPUT[20][20]; 756 extern IDentifierTable idTbl[30];//定義全局標識符表
757 extern SymbolTbl symbol[100];//定義符號表
758 extern char SymbolTbl_Define[15]; 759
760 //創建堆棧,准備對數據進行處理
761 #define MAXSIZE 300
762 //創建模板棧
763 template <typename T>
764 class Stack{ 765 public: 766 Stack(){top=-1;}//構造函數,進行初始化操作
767 ~Stack(){}//析構函數
768 bool IsEmpty(){ 769 if(top==-1) 770 return true; 771 else
772 return false; 773 }//判斷棧是否為空 774
775 //返回棧頂元素
776 T gettop() 777 { 778 return a[top]; 779 } 780 //得到棧頂指針的數值
781 int getTopPointer() 782 { 783 return top; 784 } 785 //定義遍歷整個棧的能力
786 T traverseStack(int pointer) 787 { 788 //若pointer<0則報錯
789 if(pointer<0) 790 { 791 cout<<"已到棧底,溢出!"<<endl; 792 exit(0);//退出,分析失敗
793 } 794 if(pointer>top) 795 { 796 cout<<"試圖訪問棧外元素,超界!"<<endl; 797 exit(0);//退出,分析失敗
798 } 799 return a[pointer];//返回元素值
800 } 801 //將元素壓棧
802 void push(T num) 803 { 804 if(top>=MAXSIZE) 805 return; 806 a[++top]=num; 807 } 808 //將元素彈出棧
809 T pop() 810 { 811 if(top==-1) 812 { 813 cout<<"棧已空,不能再彈"<<endl; 814 exit(0); 815 } 816 return a[top--]; 817 } 818 void Clear_Stack() 819 {//清空堆棧的能力
820 top=-1; 821 } 822 private: 823 T a[MAXSIZE]; 824 int top; 825 }; 826 /*單詞種別碼設計: 827 = 1 828 ? 2 829 + 3 830 - 4 831 * 5 832 / 6 833 ( 7 834 ) 8 835 v 9 836 c 10 837 clear 11 838 # 12 839 N 13 840 */
841 //char SymbolTbl_Define[15]={'=','\?','+','-','*','/','(',')','v','c','l','#','N','\0'};
842
843
844 /********經過以下連個函數就可以知道種別碼對應的字符在符號表中的位置************/
845 char SearchSyn(int syn) 846 {//根據種別碼返回相應字符
847 return SymbolTbl_Define[syn-1]; 848 } 849 char Prior_Position[13]={'+','-','*','/','(',')','v','c','=','?','#','\0'}; 850 int Search_Priority_Setxy(char ch) 851 {//搜索一個字符在優先符表中的位置
852 for(int i=0;i<11;i++) 853 { 854 if(ch==Prior_Position[i]) 855 { 856 return i+1;//返回該位置
857 } 858 } 859 return -1;//失敗則提示錯誤
860 } 861 /*******************************************************************/
862 Stack<SymbolTbl> Reource_Symbol;//定義堆棧
863 void Print_Context(int Stack_Top,int Sym_Pointer) 864 {//打印出當前堆棧和符號表的上下文
865 int syn,value,sym_length; 866 cout<<"\t當前堆棧情況為"<<endl; 867 cout<<"\t\t"; 868 for(int i=0;i<=Stack_Top;i++) 869 {//打印出堆棧
870 syn=Reource_Symbol.traverseStack(i).syn; 871 value=Reource_Symbol.traverseStack(i).value; 872 cout<<"("<<syn<<","<<value<<") "; 873 } 874 cout<<endl<<"\t當前符號表情況為"<<endl; 875 cout<<"\t\t"; 876 for(i=1;symbol[i].syn!=12;i++) 877 ;//計算符號表的長度i
878 sym_length=i+1;//長度,最后一個為#,從1開始數
879 for(int j=Sym_Pointer;j<sym_length;j++) 880 {//打印出堆棧
881 syn=symbol[j].syn; 882 value=symbol[j].value; 883 cout<<"("<<syn<<","<<value<<") "; 884 } 885 cout<<endl; 886 } 887 void MainProc_Analysis() 888 {//現在的情況是已經分析出了句子的單詞,並且變成了單詞塊,存放在SymbolTbl symbol[100];中 889 //並且分析出了標識符存放在了IDentifierTable idTbl[30];中 890 //分析出了算符優先分析的優先關系表存放在char PriorityTable[20][20];中 891 //已經編碼出了種別碼表,放在char SymbolTbl_Define[15];中
892
893 cout<<"下面開始核心算法:"<<endl; 894 int i; 895 int row_prior,column_prior;//行列定位指針
896 char Prior_Relation;//優先關系
897 int sym_length;//符號表長度
898 int pStackTop;//棧頂指針
899 int pScanStack;//定義掃描堆棧指針
900 int Statement_count=0;//記錄步驟序號 901
902 //現在的算法是1.初始化棧,在棧中放入#(12,),然后開始分析
903 SymbolTbl temp_sym; 904 temp_sym.syn=12; 905 temp_sym.value=-1;//-1代表這個地方不需要
906 Reource_Symbol.push(temp_sym);//將#壓棧 907
908 //對symbol中的內容定義指針,並且略過第一個#
909 int Sym_scan_pointer=1;//從一開始
910 for(i=1;symbol[i].syn!=12;i++) 911 ;//計算符號表的長度i
912 sym_length=i+1;//長度,最后一個為#,從1開始數 913 //判斷符號表第一個元素的syn==11?即是否為clear
914 if(sym_length>=3&&(symbol[1].syn==11)) 915 {//清除語句,清除屏幕並且清空標號表中的值
916 Clear_IDentifier_Tbl(); 917 Clear_Symbol_Tbl(); 918 Reource_Symbol.Clear_Stack(); 919 system("cls"); 920 goto end; 921 } 922 //下面比較棧中元素和符號表中元素的大小關系
923 pStackTop=Reource_Symbol.getTopPointer(); 924 pScanStack=pStackTop;//掃描指針從棧頂開始
925 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 926 while(1) 927 { 928 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn));//棧掃描指針
929 column_prior=Search_Priority_Setxy(SearchSyn(symbol[Sym_scan_pointer].syn));//符號表掃描指針
930 Prior_Relation=PriorityTable[row_prior][column_prior]; 931 if(Prior_Relation=='<'||(Prior_Relation=='='&&(column_prior!=11||row_prior!=11))) 932 {//要是為小於或等於關系,則進棧 933 //這里的等於關系可能為(N),v=,這兩種關系
934 Reource_Symbol.push(symbol[Sym_scan_pointer]);//棧頂++ 935 //掃描指針后移,不能回頭
936 Sym_scan_pointer++; 937 //注意此時還要改變堆棧指針的值,讓我調試了好久。。。
938 pStackTop=Reource_Symbol.getTopPointer();//得到變動后的棧頂指針,始終指向棧頂
939 pScanStack=pStackTop;//進行下一次判斷,此時掃描指針指向新移進來的元素
940 cout<<Statement_count++<<".這里是小於、等於關系,壓棧!"<<endl; 941 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 942 } 943 else if(Prior_Relation=='='&&column_prior==11&&row_prior==11) 944 {//規約到最后#N,查看棧中是否滿足情況 945 //輸出N的數值並結束
946 int final_value; 947 if(Reource_Symbol.gettop().syn==13&&(Reource_Symbol.getTopPointer()==1)) 948 {//若滿足#N的情況,則歸約成功!
949 cout<<Statement_count++<<"."<<"歸約成功!!!"<<endl; 950 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 951 final_value=Reource_Symbol.gettop().value; 952 printf("最后的結果為 %d \n",final_value); 953 Reource_Symbol.Clear_Stack(); 954 break;//退出大循環,唯一的正確出口
955 } 956 else
957 { 958 cout<<"句子錯誤,程序結束!"<<endl; 959 exit(0); 960 } 961 } 962 else if(Prior_Relation=='>') 963 {//注意下面兩句話要放在循環之前!!!
964 pStackTop=Reource_Symbol.getTopPointer();//得到棧頂指針
965 pScanStack=pStackTop;//掃描指針從棧頂開始
966 while(Prior_Relation=='>') 967 { //若優先關系始終為大於,則堆棧指針前移,不斷尋找可歸約串 968 //因為可歸約串始終在棧頂的某段區域,那么就要試探着先看一下棧頂元素是不是 969 //若棧頂元素可歸約則直接歸約為N(13,),否則查看棧頂方向的兩個元素同樣道理 970 //因為文法的產生式右部最多只有三個元素,因此若是到了三個元素還沒有搜索到 971 //可歸約串,則視為語法錯誤,停止分析 972 //此處構建兩個指針,一個既是棧頂指針,另一個為掃描可歸約串的指針 973
974
975 //這里不可能出現'(' 976
977 //判斷棧頂元素是否可歸約
978 int judge; 979 judge=Reource_Symbol.traverseStack(pScanStack).syn;//判斷該種別碼 980 //若是單個變量或常量則規約為N 981 //此處還要進行語義分析,對於標識符要查標識符表判斷是否存在,若不存在則為錯
982 if(judge==9) 983 { 984 if(idTbl[Reource_Symbol.traverseStack(pScanStack).value].value==-1) 985 { 986 cout<<"此變量可能沒被定義,按照-1處理"<<endl; 987 } 988 if(Reource_Symbol.traverseStack(pScanStack).value<0) 989 {//若該變量的符號表入口地址為負值,則說明查無此元素,則認為語法出錯,變量未定義
990 cout<<"該變量未定義"<<endl; 991 exit(0); 992 } 993 else
994 {//否則將該標識符規約為N
995 temp_sym.syn=13; 996 //獲得符號表中關於該變量的值
997 temp_sym.value=idTbl[Reource_Symbol.traverseStack(pScanStack).value].value; 998 Reource_Symbol.pop();//先彈出棧頂元素
999 Reource_Symbol.push(temp_sym);//將N壓棧
1000 pStackTop=Reource_Symbol.getTopPointer();//得到變動后的棧頂指針,始終指向棧頂 1001 //掃描指針要前移,因為要分析終結符,而棧頂已變成非終結符
1002 pScanStack=Reource_Symbol.getTopPointer()-1;//進行下一次判斷
1003 if(pScanStack<0) 1004 { 1005 cout<<"句子錯誤,程序結束!"<<endl; 1006 exit(0); 1007 } 1008 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1009 Prior_Relation=PriorityTable[row_prior][column_prior];//此處列是不會變的,因為大於關系
1010 cout<<Statement_count++<<"."<<"此處將變量規約為N"<<endl; 1011 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1012 } 1013 } 1014 else if(judge==10) 1015 {//若是常量,則要記住該常量值以便進行規約時賦值
1016 temp_sym.syn=13; 1017 temp_sym.value=Reource_Symbol.traverseStack(pScanStack).value; 1018 Reource_Symbol.pop();//先彈出棧頂元素
1019 Reource_Symbol.push(temp_sym);//將N壓棧
1020 pStackTop=Reource_Symbol.getTopPointer(); 1021 pScanStack=Reource_Symbol.getTopPointer()-1;//進行下一次判斷
1022 if(pScanStack<0) 1023 { 1024 cout<<"句子錯誤,程序結束!"<<endl; 1025 exit(0); 1026 } 1027 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1028 Prior_Relation=PriorityTable[row_prior][column_prior];//同標識符
1029 cout<<Statement_count++<<"."<<"此處將常量規約為N"<<endl; 1030 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1031 } 1032 else if(judge==1) 1033 {//v=E,此時要進行賦值操作,然后規約
1034 if(Reource_Symbol.traverseStack(pScanStack-1).syn==9)//一定為9
1035 {//若前面為標識符則正確,應該為該變量賦值 1036 //語義分析
1037 idTbl[Reource_Symbol.traverseStack(pScanStack-1).value].value=Reource_Symbol.gettop().value;//此時棧頂必為E
1038 temp_sym.syn=13; 1039 temp_sym.value=Reource_Symbol.gettop().value; //N中的值永遠為真正的值,而不是地址 1040 //開始歸約
1041 Reource_Symbol.pop(); 1042 Reource_Symbol.pop(); 1043 Reource_Symbol.pop();//彈出三個元素
1044 Reource_Symbol.push(temp_sym);//規約為N
1045 pStackTop=Reource_Symbol.getTopPointer(); 1046 pScanStack= Reource_Symbol.getTopPointer()-1;//始終指向規約后的第一個非終結符
1047 if(pScanStack<0) 1048 { 1049 cout<<"句子錯誤,程序結束!"<<endl; 1050 exit(0); 1051 } 1052 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1053 Prior_Relation=PriorityTable[row_prior][column_prior];//進行下次判斷
1054 cout<<Statement_count++<<"."<<"此處遇到等號要將v=E規約為N,程序面臨結束!"<<endl; 1055 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1056 } 1057 else
1058 { 1059 cout<<"等號前面應該有變量!"<<endl; 1060 exit(0); 1061 } 1062 }//end if(judge==1)
1063 else if(judge==3) 1064 {//+,此時棧頂應該為N+N的形式 1065 //只需把兩個數相加,並且把結果放在N中即可
1066 if(Reource_Symbol.traverseStack(pScanStack-1).syn==13&&Reource_Symbol.traverseStack(pScanStack+1).syn==13)//一定為13
1067 {//若前面為N並且棧頂為N則正確 1068 //語義分析,newtemp()
1069 temp_sym.syn=13; 1070 temp_sym.value=Reource_Symbol.traverseStack(pScanStack-1).value+Reource_Symbol.traverseStack(pScanStack+1).value; //N中的值永遠為真正的值,而不是地址 1071 //開始歸約
1072 Reource_Symbol.pop(); 1073 Reource_Symbol.pop(); 1074 Reource_Symbol.pop();//彈出三個元素
1075 Reource_Symbol.push(temp_sym);//規約為N
1076 pStackTop=Reource_Symbol.getTopPointer(); 1077 pScanStack= Reource_Symbol.getTopPointer()-1;//始終指向規約后的第一個非終結符
1078 if(pScanStack<0) 1079 { 1080 cout<<"句子錯誤,程序結束!"<<endl; 1081 exit(0); 1082 } 1083 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1084 Prior_Relation=PriorityTable[row_prior][column_prior];//進行下次判斷
1085 cout<<Statement_count++<<"."<<"此處為加法運算,將N+N規約為N,並進行語義分析!"<<endl; 1086 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1087 } 1088 else
1089 { 1090 cout<<"加號兩邊應該都有變量,句子錯誤!"<<endl; 1091 exit(0); 1092 } 1093 } 1094 else if(judge==4) 1095 {//-
1096 if(Reource_Symbol.traverseStack(pScanStack-1).syn==13&&Reource_Symbol.traverseStack(pScanStack+1).syn==13)//一定為13
1097 {//若前面為N並且棧頂為N則正確 N-N 1098 //語義分析,newtemp()
1099 temp_sym.syn=13; 1100 //注意運算的順序
1101 temp_sym.value=Reource_Symbol.traverseStack(pScanStack-1).value-Reource_Symbol.traverseStack(pScanStack+1).value; //N中的值永遠為真正的值,而不是地址 1102 //開始歸約
1103 Reource_Symbol.pop(); 1104 Reource_Symbol.pop(); 1105 Reource_Symbol.pop();//彈出三個元素
1106 Reource_Symbol.push(temp_sym);//規約為N
1107 pStackTop=Reource_Symbol.getTopPointer(); 1108 pScanStack= Reource_Symbol.getTopPointer()-1;//始終指向規約后的第一個非終結符
1109 if(pScanStack<0) 1110 { 1111 cout<<"句子錯誤,程序結束!"<<endl; 1112 exit(0); 1113 } 1114 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1115 Prior_Relation=PriorityTable[row_prior][column_prior];//進行下次判斷
1116 cout<<Statement_count++<<"."<<"此處為減法運算,將N-N規約為N,並進行語義分析!"<<endl; 1117 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1118 } 1119 else
1120 { 1121 cout<<"減號兩邊應該都有變量,句子錯誤!"<<endl; 1122 exit(0); 1123 } 1124 } 1125 else if(judge==5) 1126 {//*
1127 if(Reource_Symbol.traverseStack(pScanStack-1).syn==13&&Reource_Symbol.traverseStack(pScanStack+1).syn==13)//一定為13
1128 {//若前面為N並且棧頂為N則正確 N*N 1129 //語義分析,newtemp()
1130 temp_sym.syn=13; 1131 //注意運算的順序
1132 temp_sym.value=Reource_Symbol.traverseStack(pScanStack-1).value*Reource_Symbol.traverseStack(pScanStack+1).value; //N中的值永遠為真正的值,而不是地址 1133 //開始歸約
1134 Reource_Symbol.pop(); 1135 Reource_Symbol.pop(); 1136 Reource_Symbol.pop();//彈出三個元素
1137 Reource_Symbol.push(temp_sym);//規約為N
1138 pStackTop=Reource_Symbol.getTopPointer(); 1139 pScanStack= Reource_Symbol.getTopPointer()-1;//始終指向規約后的第一個非終結符
1140 if(pScanStack<0) 1141 { 1142 cout<<"句子錯誤,程序結束!"<<endl; 1143 exit(0); 1144 } 1145 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1146 Prior_Relation=PriorityTable[row_prior][column_prior];//進行下次判斷
1147 cout<<Statement_count++<<"."<<"此處為乘法運算,將N*N規約為N,並進行語義分析!"<<endl; 1148 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1149 } 1150 else
1151 { 1152 cout<<"乘號兩邊應該都有變量,句子錯誤!"<<endl; 1153 exit(0); 1154 } 1155 } 1156 else if(judge==6) 1157 {// /除
1158 if(Reource_Symbol.traverseStack(pScanStack-1).syn==13&&Reource_Symbol.traverseStack(pScanStack+1).syn==13)//一定為13
1159 {//若前面為N並且棧頂為N則正確 N*N 1160 //語義分析,newtemp()
1161 temp_sym.syn=13; 1162 //注意運算的順序
1163 temp_sym.value=Reource_Symbol.traverseStack(pScanStack-1).value / Reource_Symbol.traverseStack(pScanStack+1).value; //N中的值永遠為真正的值,而不是地址 1164 //開始歸約
1165 Reource_Symbol.pop(); 1166 Reource_Symbol.pop(); 1167 Reource_Symbol.pop();//彈出三個元素
1168 Reource_Symbol.push(temp_sym);//規約為N
1169 pStackTop=Reource_Symbol.getTopPointer(); 1170 pScanStack= Reource_Symbol.getTopPointer()-1;//始終指向規約后的第一個非終結符
1171 if(pScanStack<0) 1172 { 1173 cout<<"句子錯誤,程序結束!"<<endl; 1174 exit(0); 1175 } 1176 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1177 Prior_Relation=PriorityTable[row_prior][column_prior];//進行下次判斷
1178 cout<<Statement_count++<<"."<<"此處為除法運算,將N/N規約為N,並進行語義分析!"<<endl; 1179 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1180 } 1181 else
1182 { 1183 cout<<"除號兩邊應該都有變量,句子錯誤!"<<endl; 1184 exit(0); 1185 } 1186 } 1187 else if(judge==8) 1188 {// ) 若為右括號,這種情況一旦出現就要將(N)規約為N 1189 //判斷前面是不是“(N”
1190 if(Reource_Symbol.traverseStack(pScanStack-1).syn==13&&Reource_Symbol.traverseStack(pScanStack-2).syn==7) 1191 {//若前面為(N 則正確 1192 //語義分析,newtemp()
1193 temp_sym.syn=13; 1194 //N<-(N),N.value=(N).value
1195 temp_sym.value=Reource_Symbol.traverseStack(pScanStack-1).value; //N中的值永遠為真正的值,而不是地址 1196 //開始歸約
1197 Reource_Symbol.pop(); 1198 Reource_Symbol.pop(); 1199 Reource_Symbol.pop();//彈出三個元素
1200 Reource_Symbol.push(temp_sym);//規約為N
1201 pStackTop=Reource_Symbol.getTopPointer(); 1202 pScanStack= Reource_Symbol.getTopPointer()-1;//始終指向規約后的第一個非終結符
1203 if(pScanStack<0) 1204 { 1205 cout<<"句子錯誤,程序結束!"<<endl; 1206 exit(0); 1207 } 1208 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1209 Prior_Relation=PriorityTable[row_prior][column_prior];//進行下次判斷
1210 cout<<Statement_count++<<"."<<"此處為),將(N)規約為N,並進行語義分析!"<<endl; 1211 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1212 } 1213 else
1214 { 1215 cout<<"括號不匹配,句子錯誤!"<<endl; 1216 exit(0); 1217 } 1218 } 1219 else if(judge==2) 1220 {// ? 則問號前面必為N
1221 if(Reource_Symbol.traverseStack(pScanStack-1).syn==13) 1222 { 1223 //語義分析,newtemp()
1224 temp_sym.syn=13; 1225 temp_sym.value=Reource_Symbol.traverseStack(pScanStack-1).value; 1226 //開始歸約
1227 Reource_Symbol.pop(); 1228 Reource_Symbol.pop();//彈出2個元素
1229 Reource_Symbol.push(temp_sym);//規約為N
1230 pStackTop=Reource_Symbol.getTopPointer(); 1231 pScanStack= Reource_Symbol.getTopPointer()-1;//始終指向規約后的第一個非終結符
1232 if(pScanStack<0) 1233 { 1234 cout<<"句子錯誤,程序結束!"<<endl; 1235 exit(0); 1236 } 1237 row_prior=Search_Priority_Setxy(SearchSyn(Reource_Symbol.traverseStack(pScanStack).syn)); 1238 Prior_Relation=PriorityTable[row_prior][column_prior];//進行下次判斷
1239 cout<<Statement_count++<<"."<<"此處將 N? 規約為N"<<endl; 1240 Print_Context(Reource_Symbol.getTopPointer(),Sym_scan_pointer); 1241 } 1242 else
1243 { 1244 cout<<"問號前面必須有N!"<<endl; 1245 exit(0); 1246 } 1247 } 1248 else
1249 { 1250 cout<<"暫時還未定義"<<endl; 1251 exit(0); 1252 } 1253 }// end while(Prior_Relation=='>')
1254 }//end else if(Prior_Relation=='>')
1255 }//end while(1)
1256 end: ; 1257 }//end
1258
1259 /*單詞種別碼設計: 1260 = 1 1261 ? 2 1262 + 3 1263 - 4 1264 * 5 1265 / 6 1266 ( 7 1267 ) 8 1268 v 9 1269 c 10 1270 clear 11 1271 # 12 1272 N 13 1273 */
1274
1275 /**********#include "MainProc_Analysis.h"*****************/
1276
1277
1278
1279
1280
1281 主程序: 1282 #include "stdio.h"
1283 #include "firstVT_lastVT.h"//計算firstVT()和LastVT()的程序
1284 #include "GetPriorityTable.h"//創建算符優先關系表的程序
1285 #include "Simple_Lexical_Analysis.h"//詞法分析的程序
1286 #include "MainProc_Analysis.h" //主分析程序,用來進行語法和語義分析
1287 /*注意此處l是clear的縮寫,簡略標記,因為在后續算符優先分析中它和其他任何非終結符(除#外)沒任何關系,故分別對待 1288 S-->v=E|E?|l 1289 E-->E+T|E-T|T 1290 T-->T*F|T/F|F 1291 F-->(E)|v|c 1292 10個終結符,需要構造priorTable[12][12]大小的表 1293 + - * / ( ) v c = ? # 外加一個結束符# 1294 */
1295
1296 //設置為全局變量,則字符串開始時被全部初始化為'\0' 1297 //用於存儲FIRSTVT集
1298 char FIRSTVT[20][20]; 1299 char LASTVT[20][20]; 1300 char PriorityTable[20][20];//優先符表
1301 char INPUT[20][20]; //文法記錄表
1302 IDentifierTable idTbl[30];//定義全局標識符表
1303 SymbolTbl symbol[100];//定義符號表,記錄輸入的程序片段
1304 char SymbolTbl_Define[15]={'=','\?','+','-','*','/','(',')','v','c','l','#','N','\0'};//定義各個終結符的syn
1305
1306 int main() 1307 { 1308 char ch;//是否繼續的標記 1309 //計算並顯示firstVT()和LastVT()
1310 DisplayFirstVT_LasVT(); 1311 //創建算符優先關系表
1312 createPriorityTable(); 1313 //打印算符優先關系表
1314 DisplayPriorityTable(); 1315 //cout<<"請輸入語句的個數"<<endl;
1316 while(1) 1317 { 1318 //首先要清空符號表,這樣才能進行下一輪分析
1319 Clear_Symbol_Tbl(); 1320 //詞法分析,登記符號表
1321 LexicalAnalysisCtl(); 1322 //語法語義分析,產生運行結果
1323 MainProc_Analysis(); 1324 cout<<"continue? y or n"<<endl; 1325 cin>>ch; 1326 if(!(ch=='y')&&!(ch=='Y')) 1327 { 1328 cout<<"the procedure is end"<<endl; 1329 exit(0); 1330 } 1331 } 1332 return 0; 1333 }