編譯原理實驗一 詞法分析器C++實現


//前言:作者很菜,深知這不是最快也不是最簡潔的代碼,但都是自己分析得到的, 僅供大家參考,共同進步。如果有改進意見歡迎提出,不對的地方也歡迎指正。

一. 題目分析

根據題目要求,C語言子集分為五類:

第一類:標識符,通常來說指函數名、變量名,就是編程者自己命名的這些內容,不過在后續的測試中發現printf("   ")雙引號中的內容也全部屬於這一類

第二類:常數

第三類:保留字(即關鍵字),本實驗中32個

第四類:界符,如/* ( ) { }

第五類:運算符,如< <= > >=

題目的思路是對輸入的一段代碼逐詞解析。輸出形式為“計數: <符號名,符號標號>”。

 

二. 示例輸出

需要注意的是,輸入最后一行后面沒有'\n'。

 

 三. 實驗過程 

//注意這里涉及的部分變量是全局定義的,如果有不明白的,見文末的源代碼。

首先將代碼讀入,這個標准輸入函數注明了不允許更改,因此不關心它如何從prog.txt中讀取輸入,只要知道string類型的prog中現在有全部輸入即可。

void read_prog(string& prog)
{
	char c;
	while(scanf("%c",&c)!=EOF){
		prog += c;
	}
}

然而這種輸入並不理想,我想得到的是以'\n'分界的字符串,因此對它進行一次處理。把它按行放入了ScanBuffer[]中,該字符串數組中每一行的最后一個元素為'\n'。

注意這里最后一行必須要加,因為我們后面的while判斷中只有讀到最后一行最后一個'\n'才能結束。

for(int i = 0; i < prog.length(); i++){
         ScanBuffer[j].push_back(prog[i]);
         if(prog[i] == '\n')
            j++;
	}

 ScanBuffer[j].push_back('\n');

 

接下來把最后一行下標j作為endrow,開始while循環,對所有字符進行分類。循環條件為

 while(row!=endrow||ScanBuffer[row][col]!='\n')

即行數為最后一行且ScanBuffer中已經判斷到最后一個換行符時,結束循環。

循環開始,首先判斷當前字符ch是否是字母,如果是的話,有兩種可能,是第一類標識符或者第三類保留字。

對於標識符而言,可以有數字,不斷把它放入strToken中暫時存儲。

if(isalpha(ch)){
            while(isalpha(ch)||isdigit(ch)){
                strToken.push_back(ch);
                GetChar();
            }
            Retract();
            code = Jdg();
            //if(code != 81)
            cout<<(cnt++)<<": <"<<strToken<<','<<code<<'>'<<endl;
 }

GetChar()函數讓ch取到下一個字符,同時列數自加,方便下次讀取。

void GetChar(){
    ch = ScanBuffer[row][col];
    col++;
}

Retract函數回退一列,同時ch置為空。因為這里while循環條件不成立才能跳出,因此多GetChar了一次,此時回退。

void Retract(){
    col--;
    ch='\0';
}//回退

Judge函數判斷strToken是否為保留字,不是則按實驗要求,返回代表標識符的81。

int Jdg(){
    int i;
    for(i = 0; i < 32; i++)
        if(WordList[i] == strToken)
            return (i+1);
    return 81;
}//是否為關鍵字的判斷

至此,標識符和保留字輸出完畢

然后我們判斷是否為第二類常數。常數中只能有digit,為常數則按要求映射到80。

else if(isdigit(ch)){
            while(isdigit(ch)){
                strToken.push_back(ch);
                GetChar();
            }
            Retract();
            cout<<(cnt++)<<": <"<<strToken<<','<<80<<'>'<<endl;
}

接下來是對第四類界符和第五類運算符的判斷,通過多個else if並列得到。

以'-'  '--'  '-='  '->'為例。如果第一個字符為'-',通過GetChar取它下一個字符,判斷。注意else中需要回退一下,因為此時取到了下一個字符。

else if(ch == '-'){
            GetChar();
            if(ch=='-')
                cout<<(cnt++)<<": <--,34>"<<endl;
            else if(ch=='=')
                cout<<(cnt++)<<": <-=,35>"<<endl;
            else if(ch=='>')
                cout<<(cnt++)<<": <->,36>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <-,33>"<<endl;
            }
}

這里有兩個特殊情況,一個是注釋'//'和'/**'的判斷,需要與'/'和'/='放在一起。

如果發現了'//',則該行接下來的部分都是注釋,需要作為注釋輸出,並映射到題中要求的79。因此不斷GetChar,while循環判斷ch下一個字符是不是'\n'(不判斷ch是為了不越界),只要不是,就不斷輸出。

如果發現了'/*',則它與'*/'之間的部分為注釋,最后的GetChar()對輸出沒有影響,但我們需要讓col只想待解決的內容。

else if(ch == '/'){
            GetChar();
            if(ch=='=')
                cout<<(cnt++)<<": </=,51>"<<endl;
            else if(ch=='/'){
                //注釋部分
                cout<<(cnt++)<<": </";
                while(ScanBuffer[row][col] != '\n'){
                    cout << ch ;
                    GetChar();
                }
                cout << ch;
                cout <<",79>" << endl;
            }
            else if(ch=='*'){
                /*注釋部分*/
                cout<<(cnt++)<<": </";
                while(ScanBuffer[row][col] != '*'){
                    cout << ch ;
                    GetChar();
                }
                cout << ch;
                GetChar();
                GetChar();
                cout <<"*/,79>" << endl;
            }
            else{
                Retract();
                cout<<(cnt++)<<": </,50>"<<endl;
            }
        }  

第二個特殊情況是printf中的%d,%f等,我起初覺得%333d與a%333*d會很難區分,后來意識到只要在printf(" ")的雙引號中,就是一個標識符,不論帶數字還是關鍵字,因此只要在判斷雙引號" "時將其中內容按標識符輸出即可。

else if(ch == '"'){
            cout<<(cnt++)<<": <\",78>"<<endl;
            cout<<(cnt++)<<": <";
            while(ScanBuffer[row][col] != '"'){
                    GetChar();cout << ch ;
                }
            cout<<",81>"<<endl;
            GetChar();
            cout<<(cnt++)<<": <\",78>"<<endl;
  }

實驗中沒有要求錯誤處理,因此不做考慮。

 

四.源代碼

沒有分文件,是我自己調試時的源代碼,輸入后用兩次ctrl + Z代表EOF,因為ctrl + Z接收時必須在一行的第一個才算是EOF,而實驗要求中注明了最后一行沒有換行。

頭文件是實驗給的,也不知道自己用沒用。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

using namespace std;
#define MAXROW 50

string WordList[32]=
{"auto","break","case","char","const",
"continue","default","do","double","else",
"enum","extern","float","for","goto","if",
"int","long","register","return","short",
"signed","sizeof","static","struct","switch",
"typedef","union","unsigned","void","volatile","while"};//關鍵字表
string ScanBuffer[MAXROW];//用於存放代碼的每一行
char ch;
int row=0, col=0;
int cnt = 1;
string strToken;

void read_prog(string& prog)
{
	char c;
	while(scanf("%c",&c)!=EOF){
		prog += c;
	}
}

void GetChar(){
    ch = ScanBuffer[row][col];
    col++;
}

void isN(){
    while(ch == '\n'){
        row++;
        col = 0;
        GetChar();
    }
    while(ch==' '||ch=='\t')
        GetChar();
}


int Jdg(){
    int i;
    for(i = 0; i < 32; i++)
        if(WordList[i] == strToken)
            return (i+1);
    return 81;
}//是否為關鍵字的判斷

void Retract(){
    col--;
    ch='\0';
}//回退

void Analysis()
{
	string prog;

	read_prog(prog);

	int j = 0;

	for(int i = 0; i < prog.length(); i++){
         ScanBuffer[j].push_back(prog[i]);
         if(prog[i] == '\n')
            j++;
	}

    ScanBuffer[j].push_back('\n');

	int endrow = j;

    while(row!=endrow||ScanBuffer[row][col]!='\n'){
        int code;
        strToken="";//當前字符串
        GetChar();//取下一個字符
        isN();
        if(isalpha(ch)){
            while(isalpha(ch)||isdigit(ch)){
                strToken.push_back(ch);
                GetChar();
            }
            Retract();
            code = Jdg();
            //if(code != 81)
            cout<<(cnt++)<<": <"<<strToken<<','<<code<<'>'<<endl;
        }

        else if(isdigit(ch)){
            while(isdigit(ch)){
                strToken.push_back(ch);
                GetChar();
            }
            Retract();
            cout<<(cnt++)<<": <"<<strToken<<','<<80<<'>'<<endl;
        }
        else if(ch == '-'){
            GetChar();
            if(ch=='-')
                cout<<(cnt++)<<": <--,34>"<<endl;
            else if(ch=='=')
                cout<<(cnt++)<<": <-=,35>"<<endl;
            else if(ch=='>')
                cout<<(cnt++)<<": <->,36>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <-,33>"<<endl;
            }
        }
        else if(ch == '!'){
            GetChar();
            if(ch=='=')
                cout<<(cnt++)<<": <!=,38>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <!,37>"<<endl;
            }
        }
        else if(ch == '%'){
            GetChar();
            if(ch=='=')
                cout<<(cnt++)<<": <%=,40>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <%,39>"<<endl;
            }
        }
        else if(ch == '&'){
            GetChar();
            if(ch=='&')
                cout<<(cnt++)<<": <&&,42>"<<endl;
            else if(ch=='=')
                cout<<(cnt++)<<": <&=,43>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <&,41>"<<endl;
            }
        }
        else if(ch == '(')
            cout<<(cnt++)<<": <(,44>"<<endl;
        else if(ch == ')')
            cout<<(cnt++)<<": <),45>"<<endl;
        else if(ch == '*'){
            GetChar();
            if(ch=='=')
                cout<<(cnt++)<<": <*=,47>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <*,46>"<<endl;
            }
        }
        else if(ch == ',')
            cout<<(cnt++)<<": <,,48>"<<endl;
        else if(ch == '.')
            cout<<(cnt++)<<": <.,49>"<<endl;
        else if(ch == '/'){
            GetChar();
            if(ch=='=')
                cout<<(cnt++)<<": </=,51>"<<endl;
            else if(ch=='/'){
                //注釋部分
                cout<<(cnt++)<<": </";
                while(ScanBuffer[row][col] != '\n'){
                    cout << ch ;
                    GetChar();
                }
                cout << ch;
                cout <<",79>" << endl;
            }
            else if(ch=='*'){
                /*注釋部分*/
                cout<<(cnt++)<<": </";
                while(ScanBuffer[row][col] != '*'){
                    cout << ch ;
                    GetChar();
                }
                cout << ch;
                GetChar();
                GetChar();
                cout <<"*/,79>" << endl;
            }
            else{
                Retract();
                cout<<(cnt++)<<": </,50>"<<endl;
            }
        }
        else if(ch == ':')
            cout<<(cnt++)<<": <:,52>"<<endl;
        else if(ch == ';')
            cout<<(cnt++)<<": <;,53>"<<endl;
        else if(ch == '?')
            cout<<(cnt++)<<": <?,54>"<<endl;
        else if(ch == '[')
            cout<<(cnt++)<<": <[,55>"<<endl;
        else if(ch == ']')
            cout<<(cnt++)<<": <],56>"<<endl;
         else if(ch == '^'){
            GetChar();
            if(ch=='=')
                cout<<(cnt++)<<": <^=,58>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <^,57>"<<endl;
            }
        }
        else if(ch == '{')
            cout<<(cnt++)<<": <{,59>"<<endl;
        else if(ch == '|'){
            GetChar();
            if(ch=='|')
                cout<<(cnt++)<<": <||,61>"<<endl;
            else if(ch=='=')
                cout<<(cnt++)<<": <|=,62>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <|,60>"<<endl;
            }
        }
        else if(ch == '}')
           cout<<(cnt++)<<": <},63>"<<endl;
        else if(ch == '~')
            cout<<(cnt++)<<": <~,64>"<<endl;
        else if(ch == '+'){
            GetChar();
            if(ch=='+')
               cout<<(cnt++)<<": <++,66>"<<endl;
            else if(ch=='=')
                cout<<(cnt++)<<": <+=,67>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <+,65>"<<endl;
            }
        }
        else if(ch == '<'){
            GetChar();
            if(ch=='<'){
                GetChar();
                if(ch=='=')
                    cout<<(cnt++)<<": <<<=,70>"<<endl;
                else{
                    Retract();
                    cout<<(cnt++)<<": <<<,69>"<<endl;
                }
            }
            else if(ch=='=')
               cout<<(cnt++)<<": <<=,71>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <<,68>"<<endl;
            }
        }
         else if(ch == '='){
            GetChar();
            if(ch=='=')
                cout<<(cnt++)<<": <==,73>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <=,72>"<<endl;
            }
        }
        else if(ch == '>'){
            GetChar();
            if(ch=='>'){
                GetChar();
                if(ch=='=')
                    cout<<(cnt++)<<": <>>=,77>"<<endl;
                else{
                    Retract();
                    cout<<(cnt++)<<": <>>,76>"<<endl;
                }
            }
            else if(ch=='=')
                cout<<(cnt++)<<": <>=,75>"<<endl;
            else{
                Retract();
                cout<<(cnt++)<<": <>,74>"<<endl;
            }
        }
        else if(ch == '"'){
            cout<<(cnt++)<<": <\",78>"<<endl;
            cout<<(cnt++)<<": <";
            while(ScanBuffer[row][col] != '"'){
                    GetChar();cout << ch ;
                }
            cout<<",81>"<<endl;
            GetChar();
            cout<<(cnt++)<<": <\",78>"<<endl;
        }

    }

}

int main()
{
    Analysis();
    return 0;
}

 

五. 運行結果

注:

EOF如果windows系統想在標准輸入中表示,需要用ctrl + Z.

/*

后記:寫的時候哭了兩次,第三組數據怎么都過不去,又是鎖着的看不到,完全不知道怎么改。后來發現自己的/**/注釋處理將后面的一行都算作注釋了。

也許有人說女生比較脆弱,但是對我來說,眼淚只意味着更加堅強。

幸好電腦的鍵盤縫隙不大鴨。

謹此,共勉。

*/

最后有什么錯誤歡迎大家指正。


免責聲明!

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



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