編譯原理之詞法分析器(一)


由於時間太少,偶爾才花點時間謝謝這個,廢話不多說,下面來簡單講解下詞法分析器的實現過程。

一下內容包括:

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回車:

 

 

 


免責聲明!

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



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