由於時間太少,偶爾才花點時間謝謝這個,廢話不多說,下面來簡單講解下詞法分析器的實現過程。
一下內容包括:
1:講解簡單詞法分析器的實現
2:用C語言驗證
注意:詞法分析器可以用在命令解釋器上,原理是一樣的。
首先詞法分析器的任務就是識別單詞的屬性,比如在編程語言中是關鍵字還是標識符或者是數字等等,這些工作就是詞法分析需要做的。
下面我們來通過一個非常簡單的例子來說明如何讓構建。
假設現在又一下關鍵字需要識別,其id號已經做了下面規定,如果待檢測的單詞不在其中,則視為標識符,並保存其標識符,如果在其中,則輸出id號。
關鍵字 ID uint8 1 uint16 2 uint32 3 int8 4 int16 5 int32 6 if 20 for 21 while 22 switch 23 case 24 goto 25
那么如何來識別出單詞呢?
一個簡單粗暴的方法就是直接判斷,我們將其全部定義為字符串,一個一個判斷,這種方式最簡單易懂,但是這種方式的效率極低,效率低的原因就是每次對其進行遍歷,從頭開始匹配,如果匹配返回,如果不匹配,繼續下一個,這樣時間並不確定,而且在最壞情況下的時間復雜度為O(N)。如果將其在嵌入式上面用過命令解釋器,基本不可靠。所以下面來說在利用狀態機實現該過程,時間復雜度為O(1),即不管輸入那種類型,只需要一次便可得到輸出結果,並不需要反復遍歷,這就是下面要介紹的狀態機解決方案。
首先,我們先來簡單識別幾個單詞:
例如uint8,uint16,uint32,int8,int16,int32。
第一步:構建狀態機。狀態機的構建使用狀態圖表示:
狀態圖接收狀態應該用雙圈表示,這里用不同顏色表示。
上面只涉及到了六個關鍵字的判斷。
現在的問題是如何將其進行程序描述出來,有一個比較粗暴的方式,就是利用二維數組直接表示。如下圖所示:
可以發現該方式的壞處就是非常浪費內存,很多元素都沒有用到。所以下面用另一種方式表示:
這樣就大大降低的內存的浪費。
下面簡單說下上面的思路:二維數組的行下標表示當前的狀態,列下標表示可進入的下一個狀態號,但是可進入的下一個狀態號有多個,因此還需要記錄輸入字符(也就是狀態圖上的邊),所以必須定義一個數組來存儲要匹配的字符。
由於時間原因,這里不多敘述了。
下面是設計好的狀態圖:
下面是代碼
/*** * 編寫人: * 時 間:2019.11.7 * 文件名:lexer.h * 說 明:詞法分析器 ***/ /** 下面是所有能識別的關鍵字 關鍵字 ID uint8 1 uint16 2 uint32 3 int8 4 int16 5 int32 6 if 20 for 21 while 22 switch 23 case 24 goto 25 **/ /********************** 下面定義狀態表 ***********************/ #ifndef __MYLAXER_ #define __MYLAXER_ typedef struct Lexer { int id; char Label[20]; }LexerPro; void StatusInit(void); LexerPro Analyser(char *sream); #endif
/*** * 編寫人: * 時 間:2019.11.7 * 文件名:lexer.c * 說 明:詞法分析器 ***/ #include "lexer.h" #include "stdio.h" int Status[41][7]; char TokenTab[41]="-uint813int813fforwhileswitchcasegoto6262"; void StatusInit(void) { /**S0下一狀態******/ Status[0][0]=1; //u Status[0][1]=8; //i Status[0][2]=15; //f Status[0][3]=18; //w Status[0][4]=23; //s Status[0][5]=29; //c Status[0][6]=33; //f /**S1下一狀態******/ Status[1][0]=2; //i /**S2下一狀態******/ Status[2][0]=3; //n /**S3下一狀態******/ Status[3][0]=4; //t /**S4下一狀態******/ Status[4][0]=5; //8 Status[4][1]=6; //1 Status[4][2]=7; //3 /**S5下一狀態******/ Status[5][0]=255; //接收狀態標志255 Status[5][1]=1; //uint8 /**S6下一狀態******/ Status[6][0]=37; //6 /**S7下一狀態******/ Status[7][0]=38; //2 /**S8下一狀態******/ Status[8][0]=9; //n Status[8][1]=14; //f /**S9下一狀態******/ Status[9][0]=10; //t /**S10下一狀態******/ Status[10][0]=11; //8 Status[10][1]=12; //1 Status[10][2]=13; //3 /**S11下一狀態******/ Status[11][0]=255; //接收狀態標志255 Status[11][1]=4; //int8 /**S12下一狀態******/ Status[12][0]=39; //6 /**S13下一狀態******/ Status[13][0]=40; //2 /**S14下一狀態******/ Status[14][0]=255; //接收狀態標志255 Status[14][1]=20; //if /**S15下一狀態******/ Status[15][0]=16; //o /**S16下一狀態******/ Status[16][0]=17; //r /**S17下一狀態******/ Status[17][0]=255; //接收狀態標志255 Status[17][1]=21; //for /**S18下一狀態******/ Status[18][0]=19; //h /**S19下一狀態******/ Status[19][0]=20; //i /**S20下一狀態******/ Status[20][0]=21; //l /**S21下一狀態******/ Status[21][0]=22; //e /**S22下一狀態******/ Status[22][0]=255; //接收狀態標志255 Status[22][1]=22; //while /**S23下一狀態******/ Status[23][0]=24; //w /**S24下一狀態******/ Status[24][0]=25; //i /**S25下一狀態******/ Status[25][0]=26; //t /**S26下一狀態******/ Status[26][0]=27; //c /**S27下一狀態******/ Status[27][0]=28; //h /**S28下一狀態******/ Status[28][0]=255; //接收狀態標志255 Status[28][1]=23; //switch /**S29下一狀態******/ Status[29][0]=30; //a /**S30下一狀態******/ Status[30][0]=31; //s /**S31下一狀態******/ Status[31][0]=32; //e /**S32下一狀態******/ Status[32][0]=255; //接收狀態標志255 Status[32][1]=24; //case /**S33下一狀態******/ Status[33][0]=34; //o /**S34下一狀態******/ Status[34][0]=35; //t /**S35下一狀態******/ Status[35][0]=36; //o /**S36下一狀態******/ Status[36][0]=255; //接收狀態標志255 Status[36][1]=25; //goto /**S37下一狀態******/ Status[37][0]=255; //接收狀態標志255 Status[37][1]=2; //uint16 /**S38下一狀態******/ Status[38][0]=255; //接收狀態標志255 Status[38][1]=3; //uint32 /**S39下一狀態******/ Status[39][0]=255; //接收狀態標志255 Status[39][1]=5; //int16 /**S40下一狀態******/ Status[40][0]=255; //接收狀態標志255 Status[40][1]=6; //int32 } int NxteStatus(char inChar,int CurrentStatus) { int i; for(i=0;i<7;i++) { if(inChar==TokenTab[Status[CurrentStatus][i]]) { return Status[CurrentStatus][i];//返回下一個狀態 } } return -1; //返回非接收狀態 } LexerPro Analyser(char *sream) { LexerPro resul; int error=0; int StartSta=0; //狀態 int TemtSta=0; //臨時狀態 int count=0; StatusInit(); while(*(sream)!='\0'&&(*(sream)!=' ')) { resul.Label[count++]=*(sream); if(!error) { TemtSta=NxteStatus(*sream,StartSta); } printf("當前狀態=%d\n",TemtSta); if(Status[TemtSta][0]==255) //表示達到接收狀態 { error=1; //禁止進入下個狀態 if((*(sream+1)==' ')||(*(sream+1)=='\0')) //如果下一個字符為空格,表示接收 { resul.id=Status[TemtSta][1]; //返回id break; } else //如果下一個字符為不為空格,表示包含關鍵字但不是關鍵字 { resul.id=0; TemtSta=0; } } if(TemtSta==-1) { error=1; TemtSta=0; } if(TemtSta>0&&TemtSta<41) { StartSta=TemtSta; //更新狀態 } sream++; } resul.Label[count++]='\0'; return resul; }
#include "stdio.h" #include "lexer.h" int main(void) { LexerPro res; char input[20]=""; gets(input); res=Analyser(input); if(res.id==0) { printf("id=%d\n",res.id); puts("此單詞為標識符"); puts(res.Label); } if(res.id!=0) { printf("此單詞為關鍵字,關鍵字索引為 %d",res.id); } return 0; }
運行后如下所示:輸入for后回車
重新運行,輸入fori回車: