编译原理实验一 词法分析器C++实现

//前言:作者很菜,深知这不是最快也不是最简洁的代码,但都是自己分析得到的, 仅供大家参考,共同进步。如果有改进意见欢迎提出,不对的地方也欢迎指正。

一. 题目分析

根据题目要求,C语言子集分为五类:

第一类:标识符,通常来说指函数名、变量名,就是编程者自己命名的这些内容,不过在后续的测试中发现printf("   ")双引号中的内容也全部属于这一类

第二类:常数

第三类:保留字(即关键字),本实验中32个

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

第五类:运算符,如< <= > >=

题目的思路是对输入的一段代码逐词解析。输出形式为“计数: <符号名,符号标号>”。

 

二. 示例输出

需要注意的是,输入最后一行后面没有'n'。编译原理实验一 词法分析器C++实现编译原理实验一 词法分析器C++实现

 

 三. 实验过程 

//注意这里涉及的部分变量是全局定义的,如果有不明白的,见文末的源代码。

首先将代码读入,这个标准输入函数注明了不允许更改,因此不关心它如何从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='';
}//回退

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='';
}//回退

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;
}

 

五. 运行结果

编译原理实验一 词法分析器C++实现

注:

EOF如果windows系统想在标准输入中表示,需要用ctrl + Z.

/*

后记:写的时候哭了两次,第三组数据怎么都过不去,又是锁着的看不到,完全不知道怎么改。后来发现自己的/**/注释处理将后面的一行都算作注释了。

也许有人说女生比较脆弱,但是对我来说,眼泪只意味着更加坚强。

幸好电脑的键盘缝隙不大鸭。

谨此,共勉。

*/

最后有什么错误欢迎大家指正。

原文链接: https://www.cnblogs.com/SelmaS/p/12944670.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    编译原理实验一 词法分析器C++实现

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/350506

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 上午6:21
下一篇 2023年3月2日 上午6:22

相关推荐