利用狀態圖實現詞法分析

實驗一:詞法分析程序

                                                                                              03070020  曹寧

一.  實驗目的

基本掌握計算機語言的詞法分析程序的開發方法。

二.  實驗內容

編制一個能夠分析三種整數、標識符、主要運算符和主要關鍵字的詞法分析程序。

三.  實驗環境

PC微機

DOS操作系統或 Windows 操作系統

Turbo C 程序集成環境或 Visual C++ 程序集成環境

四.  實驗內容

1.    根據以下的正規式,編制正規文法,畫出狀態圖;

標識符

letter(letter|digit)*

letter->A|B|…|Z|a|b|…|z

digit->0|1|2|3|4|…|9

十進制整數

0 | (1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*

八進制整數

0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)*

十六進制整數

0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)*

運算符和分隔符

+  -  *  /  >  <  =  (  )  

關鍵字

if  then  else  while  do  

2.    根據狀態圖,設計詞法分析函數int nextToken(),完成以下功能:

1)              從文件讀入數據,分析出一個單詞。

2)              返回單詞種別(用整數表示),

3)              返回單詞屬性(不同的屬性可以放在不同的全局變量中)。

3.    編寫測試程序,反覆調用函數int nextToken(),輸出單詞種別和屬性。

五.  實驗步驟

1.      根據狀態圖,設計詞法分析算法

標識符          

正規式 

id->letter(letter|digit)*

letter->A|B|…|Z|a|b|…|z

digit->0|1|2|3|4|…|9

正規文法

S->aB’|bB’|…|zB’|AB’|BB’|…|ZB’

B’->0B’|1B’|…|9B’

狀態圖

             

八進制整數             

正規式 

0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)*

正規文法

S->01B|02B|…|07B

B->0B|1B|…|7B|

十進制整數             

正規式 

0 | (1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*

正規文法

S->0|1B|2B|…|9B

B->0B|1B|…|9B|

十六進制整數             

正規式 

0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)*

正規文法

S->0(x|X)(1B’|2B’|…|9B’|aB’|bB’|…|fB’|AB’|…|FB’)

B’->0B’|1B’|…|7B’|

識別這三種數字的狀態圖

             

運算符和分隔符

狀態圖

 

2.      採用C語言,設計函數scan( ),實現該算法

程序中的變量和函數聲明

//對外函數

extern void initLexer();//打開文件,初始化詞法分析器

extern int nextToken();//獲得一個token

//對外變量

extern int attr=-1;//是數值的時候存儲數值,是標識符時存儲在名字表中的位置

extern int lineNo=1;//顯示行數

extern char *keyWord[]={

        "if",

        "else",

        "then",

        "while",

        "do"

};

//內部函數

static int fail();//換狀態圖

static char getAnChar();//在文件中讀取一個字符,指針下移一位

static void ungetAnChar();//在文件中指針回退一位

static void getNum(int type);//根據type,獲得十進制數值,存儲在attr

static int lookup(const char *s);//在符號表中查找ID

static int insert(const char *s);//返回再符號表中的下標位置

static int isKeyWord(char word[]);//判斷是否爲關鍵字

//內部變量

static char lexBuf[100];//字符緩存,用來存儲當前分析的字

static int state=1, start=1;//當前狀態和狀態表的開始狀態

static int currentPos=0;//文件中當前指針的位置

static int tokenBeginning=0;//進入一個狀態表時指針位置,即換狀態表時的回退位置。

static FILE *fp;//被編譯的文件指針

static int smptableLength=0;//當前符號表的長度

 

int nextToken(){

 

    int length=0;

    char c;

    int keyWordPos=0;//在關鍵字數組中的下標

 

    state=1;start=1;

    //存儲開始位置

    tokenBeginning=currentPos;

    //狀態圖的實現

    while(1){

 

        switch(state){

        case 1:

            c=getAnChar();

            if(c==' '||c=='/t'||c=='/r'){

                tokenBeginning++;

            }

            else if(c=='/n'){

                lineNo++;

                tokenBeginning++;

            }

            else if(isalpha(c)){

                state=2;

                lexBuf[length++]=c;

            }

            else if(c==EOF) return FILEEND;

            else state=fail();

            break;

        case 2:

            c=getAnChar();

            if(isdigit(c)||isalpha(c)){

                state=2;

                lexBuf[length++]=c;

            }

            else state=3;

            break;

        case 3:

            ungetAnChar();

            lexBuf[length]='/0';

            keyWordPos=isKeyWord(lexBuf);

            if(keyWordPos!=-1){

                //是關鍵字

                return IF+keyWordPos;

            }

            //不是關鍵字

            attr=insert(lexBuf);//ID在名字表中的數組下標存儲在attr

            return ID;

        case 4:

            c=getAnChar();

            if(c=='0') state=5;

            else if(c>='1' && c<='9'){

                state=12;

                lexBuf[length++]=c;

            }

            else state=fail();

            break;

        case 5:

            c=getAnChar();

            if(c=='x') state=6;

            else if(c>='1'&& c<='8'){

                state=10;

                lexBuf[length++]=c;

            }

 

            else state=13;

            break;

        case 6:

            c=getAnChar();

            if(c>='1' && c<='9' || c>='a'&& c<='f' || c>='A' && c<='B'){

                state=7;

                lexBuf[length++]=c;

            }

            else state=fail();

            break;

        case 7:

            c=getAnChar();

            if(c>='1' && c<='9' || c>='a'&& c<='f' || c>='A' && c<='B'||c=='0'){

                state=7;

                lexBuf[length++]=c;

            }

            else state=8;

            break;

        case 8:

            ungetAnChar();

            lexBuf[length]='/0';

            getNum(INT16);//把數值存在attr

            return INT16;

        case 10:

            c=getAnChar();

            if(c>='0'&& c<='8'){

                state=10;

                lexBuf[length++]=c;

            }

            else state=11;

            break;

        case 11:

            ungetAnChar();

            lexBuf[length]='/0';

            getNum(INT8);//把數值存在attr

            return INT8;

        case 12:

            c=getAnChar();

            if(c>='0' && c<='9'){

                state=12;

                lexBuf[length++]=c;

            }

            else state=13;

            break;

        case 13:

            ungetAnChar();

            lexBuf[length]='/0';

            getNum(INT10);//把數值存在attr

            return INT10;

        case 14:

            c=getAnChar();

            if(c=='+') state=15;

            else if(c=='-') state=16;

            else if(c=='*') state=17;

            else if(c=='/') state=18;

            else if(c=='>') state=19;

            else if(c=='<') state=20;

            else if(c=='=') state=21;

            else if(c=='(') state=22;

            else if(c==')') state=23;

            else if(c==';') state=24;

            else state=fail();

            break;

        case 15:

            return ADD;

        case 16:

            return SUB;

        case 17:

            return MUL;

        case 18:

            return DIV;

        case 19:

            return GT;

        case 20:

            return LT;

        case 21:

            return EQ;

        case 22:

            return LBR;

        case 23:

            return RBR;

        case 24:

            return SEM;

        }

    }    

}

 

3.      編制測試程序(主函數main)。

void main(){

     int token;

     int oldLine=-1;

     initLexer();  

     

     while(1){

         token=nextToken();

         if(token==FILEEND) break;

         if(oldLine!=lineNo) {

             printf("___________Line %d____________/n",lineNo);

             oldLine=lineNo;

         }

        

        if(token==ID){//標識符

            printf("ID:%d,Pos:%d/n",token,attr);

        }

        else {

            switch(token){

                case INT16:case INT8:case INT10://數字

                    printf("%d/t/t%d/n",token,attr);

                    break;

                default: printf("%d/n",token);//關鍵字

                }

        }

        token=nextToken();

     }

}//*/

 

4.      調試程序:輸入一組單詞,檢查輸出結果

    1  92+data>  0x3f  04  while

1x3 x3 x3 x44   00

以上都是一行的沒有出現什麼問題,但是當對多行進行詞法分析時,總是出錯,通過打印讀到的字符發現有字符13ASCII),查資料才知是’/t’,把光標移到當前行的起始位置,我不知道爲什麼會有這個字符,添加濾掉這個字符的邏輯,才使詞法分析成功。

while (1) do num=10;

num=11;

if (1) then caoning=0x11;

if (1) than caoning=878;

 

5.      詞法分析程序的數據結構與算法

符號表的數據結構:採用的是結構數組

struct ENTYE{

   char word[100];

   float value;

};

int smptableLength=0;

struct ENTYE smptable[200];

在配以int lookup(const char *s)int insert(const char *s)對這個符號表操作。

/*查找s在符號表的位置,沒有返回-1*/

int lookup(const char *s){

    int i;

    for (i=0;i<smptableLength;i++){

        if (strcmp(smptable[i].word,s)==0) return i;

    }

    return -1;

}

/*返回s在符號表中的位置*/

int insert(const char *s){

   int i=lookup(s);

   if(i==-1){

        strcpy(smptable[smptableLength].word,s)

       //smptableLength++;

       return smptableLength++;

   }

   return i;

}

六.  心得體會

這次詞法分析的實驗本身沒有什麼難度,但是在做這實驗之前感覺沒譜,所以踏下心仔細的閱讀Aho的《編譯原理》中前三章,受到很大啓發,尤其是利用switch語句把狀態圖實現的技術,可謂一絕,這也是我學習詞法分析的最大的收穫。

在做語法分析的時候,對詞法分析進行了一些修改,學習了一下不同文件內的函數和變量的使用,在做詞法分析的時候架構沒有做好,比如哪些函數和變量作爲內部的,哪些是提供外部使用的接口,比較混亂,也沒有進行大量測試,導致語法分析時受阻重重,最後停下語法分析的編程,對此詞法分析修改,使程序清晰易讀,並進行了大量的測試,再作語法分析時就容易多了。這也是個教訓,以後編程首先得考慮好,再一步一步來。就會省很多麻煩。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章