算法描述
程序总体是把一些布尔函数写在词法分析程序之前,词法分析函数写在主函数之前。主函数中写提示用户输入,当获得一个正确的输入时,调用词法分析程序进行分析。
首先写出程序中关键字,运算符,分隔符,过滤符的集合,当单词与这些匹配时(遇到过滤符直接略过),判定是这其中的结果。遇到字母开头的单词可以分为两个小类,一是关键字,二是标识符;遇到数字开头的单词可以分为三个小类,一是定点数,二是浮点数,三是出错的标识符。如果是字母或者下划线开头的单词则是标识符。
具体的单词分析,是申请一个名为out的string,将单词结果暂存在其中,跟行数和类型一起输出。运用file的I/O将代码内容从文件中读出,以及控制光标回退一个字符。
程序结构
集合定义:关键字,运算符,分隔符,过滤符
判断函数:关键字,过滤符,字母,数字
词法分析程序
过滤:回车,换行,空格
字母开头
属于关键字
属于标识符
数字开头
属于定点数
属于浮点数
出错的标识符
字母或_开头
属于标识符
属于分隔符
属于运算符
特判两位的运算符(后面有等号的)
主程序
文字提示
用户输入
进行词法分析
文件名有误,提示重输
主要变量名说明
KEYWORD:关键字集合
OPERATOR:运算符集合
SEPERATOR:分隔符集合
FILTER:过滤符集合
ch:当前面临的一个字符
out:临时存储获取的一个字符串(单词)
InputFile:将被分析的源文件的路径和后缀
调试情况
1.数字开头的情况一开始出错了,遇到浮点数不能显示小数点后面的内容,遇到出错的标识符会报错但是会再单独输出数字和字母。思考了一下觉得是逻辑上的错误。利用在程序中插入输出当前ch值的方式,发现了错误,从而能够正确输出浮点数的值。
2.行数的显示。一开始没有想到行数要怎么去统计,后来想到可以在回车时使Line+1,于是在整个判断的开头加上对此的判断。
tip:用到了fseek函数,这个函数的定义如下:
代码如下:
#include<iostream>
#include<fstream>
#include<cstdio>
#include<windows.h>
using namespace std;
string KEYWORD[20]={"main","return","int","double","float","unsigned","if",
"else","do","while","switch","case","goto","char",
"sizeof","struct","void","continue","break","const"};
char OPERATOR[10]={'+','-','*','/','>','>=','<','<=','=','!'};
char SEPERATOR[8]={',',';','(',')','[',']','{','}'};
char FILTER[4]={' ','\t','\n'};
int Line=1;
//int FlagE,FlagF=0;
//判断关键字
bool IsKeyword(string word)
{
for(int i=0;i<20;i++)
{
if(KEYWORD[i]==word) {return true;}
}
return false;
}
//判断过滤符
bool IsFilter(char word)
{
for(int i=0;i<4;i++)
{
if(FILTER[i]==word) {return true;}
}
return false;
}
//判断字母
bool IsLetter(char word)
{
if((word>='a'&&word<='z')||(word>='A'&&word<='Z')) {return true;}
return false;
}
//判断数字
bool IsDigit(char word)
{
if(word>='0'&&word<='9') {return true;}
return false;
}
//词法分析函数
void LexicalAnalysis(FILE *fpin)
{
char ch=' ';
string out="";
while((ch=fgetc(fpin))!=EOF)
{
out="";
if(ch=='\n'){Line++;}
else if(IsLetter(ch))
{
while(IsLetter(ch))
{
out+=ch;
ch=fgetc(fpin);
}
fseek(fpin,-1,SEEK_CUR);
if(IsKeyword(out)){cout<<Line<<"\t\t\t"<<out<<"\t\t\t"<<"Keyword"<<"\t\t\t"<<endl;} //为关键字
else{cout<<Line<<"\t\t\t"<<out<<"\t\t\t"<<"Idenifier"<<"\t\t\t"<<endl;} //为标识符
}
else if(IsDigit(ch))
{
if(IsLetter(fgetc(fpin)))
{
fseek(fpin,-1,SEEK_CUR);
while(IsDigit(ch))
{
out+=ch;
ch=fgetc(fpin);
}
out+=ch;
//FlagE=1;
cout<<Line<<"\t\t\t"<<out<<"\t\t\t"<<"!!!!!Error!!!!!"<<"\t\t\t"<<endl; //为标识符出错
}
else
{
fseek(fpin,-1,SEEK_CUR);
while(IsDigit(ch)||ch=='.')
{
out+=ch;
ch=fgetc(fpin);
}
//FlagF=1;
cout<<Line<<"\t\t\t"<<out<<"\t\t\t"<<"Digit"<<"\t\t\t"<<endl; //为常数
//fseek(fpin,-1,SEEK_CUR);
}
}
else if(IsLetter(ch)||ch=='_')
{
while(IsLetter(ch)||IsDigit(ch)||ch=='_')
{
out+=ch;
ch=fgetc(fpin);
}
fseek(fpin,-1,SEEK_CUR);
cout<<Line<<"\t\t\t"<<out<<"\t\t\t"<<"Idenifier"<<"\t\t\t"<<endl; //为标识符
}
else switch(ch)
{
//符号为分隔符
case',':
case';':
case'(':
case')':
case'[':
case']':
case'{':
case'}':cout<<Line<<"\t\t\t"<<ch<<"\t\t\t"<<"Seperator"<<"\t\t\t"<<endl;break;
//符号为运算符
case'-':
case'*':
case'/':
case'=':
case'!':cout<<Line<<"\t\t\t"<<ch<<"\t\t\t"<<"Operator"<<"\t\t\t"<<endl;break;
//注意判断+=
case'+':
{
ch=fgetc(fpin);
if(ch=='=') cout<<Line<<"\t\t\t"<<"+="<<"\t\t\t"<<"Operator"<<"\t\t\t"<<endl;
}break;
//注意判断>=
case'>':
{
ch=fgetc(fpin);
if(ch=='=') cout<<Line<<"\t\t\t"<<">="<<"\t\t\t"<<"Operator"<<"\t\t\t"<<endl;
}break;
//注意判断<=
case'<':
{
ch=fgetc(fpin);
if(ch=='=') cout<<Line<<"\t\t\t"<<"<="<<"\t\t\t"<<"Operator"<<"\t\t\t"<<endl;
}break;
}
}
}
int main()
{
char InputFile[100];
FILE *fpin;
cout<<"请输入源文件的路径和后缀:";
//判断输入的文件名是否有效
while(true)
{
cin>>InputFile;
if((fpin=fopen(InputFile,"r"))!=NULL) break;
else{cout<<"文件名错误!请输入源文件的路径和后缀:";}
}
cout<<endl;
cout<<"\t\t\t"<<"--词法分析--"<<"\t\t\t"<<endl;
cout<<"-Line-"<<"\t\t\t"<<"-Word-"<<"\t\t\t"<<"-Type-"<<"\t\t\t"<<endl;
LexicalAnalysis(fpin);
return 0;
}
输入:(斜体为文件内容)
input.txt
int main()
{
int 2b,a = 10;
double b = -20.9;
if(a<=b)
a+=b;
else
a=4b+5;
return a;
}
输出: