搜索
您的当前位置:首页正文

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

来源:好走旅游网
编译原理实验⼀词法分析器C++实现

//前⾔:作者很菜,深知这不是最快也不是最简洁的代码,但都是⾃⼰分析得到的, 仅供⼤家参考,共同进步。如果有改进意见欢迎提出,不对的地⽅也欢迎指正。⼀. 题⽬分析

根据题⽬要求,C语⾔⼦集分为五类:

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

第三类:保留字(即关键字),本实验中32个第四类:界符,如/* ( ) { }第五类:运算符,如< <= > >=

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

⼆. ⽰例输出

需要注意的是,输⼊最后⼀⾏后⾯没有'\\n'。

三. 实验过程

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

⾸先将代码读⼊,这个标准输⼊函数注明了不允许更改,因此不关⼼它如何从prog.txt中读取输⼊,只要知道string类型的prog中现在有全部输⼊即可。

void read_prog(string& prog){

char c;

while(scanf(\"%c\ 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++)<<\": <\"<'<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++)<<\": <\"<'<接下来是对第四类界符和第五类运算符的判断,通过多个else if并列得到。

以'-' '--' '-=' '->'为例。如果第⼀个字符为'-',通过GetChar取它下⼀个字符,判断。注意else中需要回退⼀下,因为此时取到了下⼀个字符。

else if(ch == '-'){ GetChar(); if(ch=='-')

cout<<(cnt++)<<\": <--,34>\"<cout<<(cnt++)<<\": <-=,35>\"<')

cout<<(cnt++)<<\": <->,36>\"<Retract();

cout<<(cnt++)<<\": <-,33>\"<这⾥有两个特殊情况,⼀个是注释'//'和'/**'的判断,需要与'/'和'/='放在⼀起。

如果发现了'//',则该⾏接下来的部分都是注释,需要作为注释输出,并映射到题中要求的79。因此不断GetChar,while循环判断ch下⼀个字符是不是'\\n'(不判断ch是为了不越界),只要不是,就不断输出。

如果发现了'/*',则它与'*/'之间的部分为注释,最后的GetChar()对输出没有影响,但我们需要让col只想待解决的内容。

else if(ch == '/'){ GetChar(); if(ch=='=')

cout<<(cnt++)<<\": \"<cout<<(cnt++)<<\": while(ScanBuffer[row][col] != '\\n'){ cout << ch ; GetChar(); }

cout << ch;

cout <<\ }

else if(ch=='*'){ /*注释部分*/

cout<<(cnt++)<<\": while(ScanBuffer[row][col] != '*'){ cout << ch ; GetChar(); }

cout << ch; GetChar(); GetChar();

cout <<\"*/,79>\" << endl; } else{

Retract();

cout<<(cnt++)<<\": \"<第⼆个特殊情况是printf中的%d,%f等,我起初觉得%333d与a%333*d会很难区分,后来意识到只要在printf(\" \")的双引号中,就是⼀个标识符,不论带数字还是关键字,因此只要在判断双引号\" \"时将其中内容按标识符输出即可。

else if(ch == '\"'){

cout<<(cnt++)<<\": <\\\ cout<<(cnt++)<<\": <\";

while(ScanBuffer[row][col] != '\"'){ GetChar();cout << ch ; }

cout<<\ GetChar();

cout<<(cnt++)<<\": <\\\ }

实验中没有要求错误处理,因此不做考虑。 四.源代码

没有分⽂件,是我⾃⼰调试时的源代码,输⼊后⽤两次ctrl + Z代表EOF,因为ctrl + Z接收时必须在⼀⾏的第⼀个才算是EOF,⽽实验要求中注明了最后⼀⾏没有换⾏。

头⽂件是实验给的,也不知道⾃⼰⽤没⽤。

#include

#include #include #include #include #include using namespace std;#define MAXROW 50

string WordList[32]=

{\"auto\\"continue\\"enum\\"int\

\"signed\

\"typedef\关键字表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\ prog += c; }}

void GetChar(){

ch = ScanBuffer[row][col]; col++;}

void isN(){

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

while(ch==' '||ch=='\') 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++)<<\": <\"<'<else if(isdigit(ch)){ while(isdigit(ch)){

strToken.push_back(ch); GetChar(); }

Retract();

cout<<(cnt++)<<\": <\"<'<else if(ch == '-'){ GetChar(); if(ch=='-')

cout<<(cnt++)<<\": <--,34>\"<cout<<(cnt++)<<\": <-=,35>\"<')

cout<<(cnt++)<<\": <->,36>\"<Retract();

cout<<(cnt++)<<\": <-,33>\"<else if(ch == '!'){ GetChar(); if(ch=='=')

cout<<(cnt++)<<\": \"<Retract();

cout<<(cnt++)<<\": \"<else if(ch == '%'){ GetChar(); if(ch=='=')

cout<<(cnt++)<<\": <%=,40>\"<Retract();

cout<<(cnt++)<<\": <%,39>\"<else if(ch == '&'){ GetChar(); if(ch=='&')

cout<<(cnt++)<<\": <&&,42>\"<cout<<(cnt++)<<\": <&=,43>\"<Retract();

cout<<(cnt++)<<\": <&,41>\"<else if(ch == '(')

cout<<(cnt++)<<\": <(,44>\"<cout<<(cnt++)<<\": <),45>\"<cout<<(cnt++)<<\": <*=,47>\"<Retract();

cout<<(cnt++)<<\": <*,46>\"<else if(ch == ',')

cout<<(cnt++)<<\": <,,48>\"<cout<<(cnt++)<<\": <.,49>\"<cout<<(cnt++)<<\": \"<cout<<(cnt++)<<\": while(ScanBuffer[row][col] != '\\n'){

cout << ch ; GetChar(); }

cout << ch;

cout <<\ }

else if(ch=='*'){ /*注释部分*/

cout<<(cnt++)<<\": while(ScanBuffer[row][col] != '*'){ cout << ch ; GetChar(); }

cout << ch; GetChar(); GetChar();

cout <<\"*/,79>\" << endl; } else{

Retract();

cout<<(cnt++)<<\": \"<else if(ch == ':')

cout<<(cnt++)<<\": <:,52>\"<cout<<(cnt++)<<\": <;,53>\"<cout<<(cnt++)<<\": \"<cout<<(cnt++)<<\": <[,55>\"<cout<<(cnt++)<<\": <],56>\"<cout<<(cnt++)<<\": <^=,58>\"<Retract();

cout<<(cnt++)<<\": <^,57>\"<else if(ch == '{')

cout<<(cnt++)<<\": <{,59>\"<cout<<(cnt++)<<\": <||,61>\"<cout<<(cnt++)<<\": <|=,62>\"<Retract();

cout<<(cnt++)<<\": <|,60>\"<else if(ch == '}')

cout<<(cnt++)<<\": <},63>\"<cout<<(cnt++)<<\": <~,64>\"<cout<<(cnt++)<<\": <++,66>\"<cout<<(cnt++)<<\": <+=,67>\"<Retract();

cout<<(cnt++)<<\": <+,65>\"<else if(ch == '<'){ GetChar(); if(ch=='<'){ GetChar(); if(ch=='=')

cout<<(cnt++)<<\": <<<=,70>\"<Retract();

cout<<(cnt++)<<\": <<<,69>\"<else if(ch=='=')

cout<<(cnt++)<<\": <<=,71>\"<Retract();

cout<<(cnt++)<<\": <<,68>\"<else if(ch == '='){ GetChar(); if(ch=='=')

cout<<(cnt++)<<\": <==,73>\"<Retract();

cout<<(cnt++)<<\": <=,72>\"<else if(ch == '>'){ GetChar(); if(ch=='>'){ GetChar(); if(ch=='=')

cout<<(cnt++)<<\": <>>=,77>\"<Retract();

cout<<(cnt++)<<\": <>>,76>\"<else if(ch=='=')

cout<<(cnt++)<<\": <>=,75>\"<Retract();

cout<<(cnt++)<<\": <>,74>\"<else if(ch == '\"'){

cout<<(cnt++)<<\": <\\\ cout<<(cnt++)<<\": <\";

while(ScanBuffer[row][col] != '\"'){ GetChar();cout << ch ; }

cout<<\ GetChar();

cout<<(cnt++)<<\": <\\\ } }}

int main(){

Analysis(); return 0;}

五. 运⾏结果

注:

EOF如果windows系统想在标准输⼊中表⽰,需要⽤ctrl + Z./*

后记:写的时候哭了两次,第三组数据怎么都过不去,⼜是锁着的看不到,完全不知道怎么改。后来发现⾃⼰的/**/注释处理将后⾯的⼀⾏都算作注释了。

也许有⼈说⼥⽣⽐较脆弱,但是对我来说,眼泪只意味着更加坚强。幸好电脑的键盘缝隙不⼤鸭。谨此,共勉。*/

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

因篇幅问题不能全部显示,请点此查看更多更全内容

Top