編譯原理——算符優先分析文法(附源代碼)


算符優先分析文法

一、寫在前面

    算符優先分析文法是一種工具,在編譯的過程中,隸屬於語法分析環節,卻又與中間代碼的生成息息相關,編譯可以分為五個階段詞法分析、語法分析、語義分析(中間代碼的生成)、代碼優化、目標代碼生成。語法分析是指:在詞法分析基礎上,將單詞符號串轉化為語法單位(語法范疇)(短語、子句、句子、程序段、程序),並確定整個輸入串是否構成語法上正確的程序。也就是說語法分析是檢驗輸入串的語法是否正確,注意這里的語法正確,只是簡單地符合自己定義的規范,而不能檢測出運行時錯誤,比如"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 }
View Code

  比如說對於這個文法分析出的集合如下:

 

 

模塊二:構建優先符號表


     為了體現“優先”的關系,只是構建出了上面的兩種集合是不夠的,由上面分析我們知道了這兩個集合的用處。說白了就是為了構造出算符優先分析表。因為關系無非四類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 }
View Code

   比如說對於這個文法分析出來的優先關系表如下:

 

 

模塊三:構建詞法分析的程序


    做好了上面兩步運算已經累得不行了,下面還要繼續進行分析,那就是詞法分析程序,多虧這個程序在實驗一已經完成過,這里就拿出那里的代碼進行必要的改進和刪除,保留適合本文法要求的部分即可。其實想來無非是用到了識別標識符、識別常數、識別+、-、*、/、?、#、(、)、保留字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 }
View Code

 下面是主控程序:

 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 }
View Code

比如說對於#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; }
View Code

  好了,這次的實驗分析就到這里了。


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 }
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM