通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。
编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示)
程序输入/输出示例:
如源程序为C语言。输入如下一段:
main()
{
int a,b;
a = 10;
b = a + 20;
}
要求输出如下:
(2,”main”)
(5,”(“)
(5,”)“)
(5,”{“)
(1,”int”)
(2,”a”)
(5,”,”)
(2,”b”)
(5,”;”)
(2,”a”)
(4,”=”)
(3,”10”)
(5,”;”)
(2,”b”)
(4,”=”)
(2,”a”)
(4,”+”)
(3,”20”)
(5,”;”)
(5,”}“)
要求:
识别保留字:if、int、for、while、do、return、break、continue;
单词种别码为1。
其他的都识别为标识符;单词种别码为2。
常数为无符号整形数;单词种别码为3。
运算符包括:+、-、*、/、=、>、<、>=、<=、!= ;
单词种别码为4。
分隔符包括:,、;、{、}、(、); 单词种别码为5。
c语言的源程序存放在code.txt中,只要用到三个函数来实现词法分析:预处理函数、扫描判断单词类型的函数以及主函数.
1、在预处理函数process()中,使用文档操作函数打开源程序文件code.txt,去除两种类型(“//”,“/…/”)的注释、多余的空格合并为一个、换行符、回车符等,然后将处理后的保存在另一个新的文件result.txt中,最后关闭文档.
2、打开result.txt文件,调用扫描函数,从文件里读取一个单词调用判断单词类型的函数与之前建立的符号表进行对比判断,最后格式化输出。
#include
#include
#include
#include
#include
#include
using namespace std;
int aa;// fseek的时候用来接着的
string word="";
string reserved_word[20];//保留
char buffer;//每次读进来的一个字符
int num=0;//每个单词中当前字符的位置
int line=1; //行数
int row=1; //列数,就是每行的第几个
bool flag; //文件是否结束了
int flag2;//单词的类型
//预处理函数
int process(){
FILE *p;
int falg=0,len,i=0,j=0;
char str[1000],str1[1000],c;
if((p=fopen("code.txt","rt"))==NULL){
printf("无法打开要编译的源程序");
return 0;
}else{
while((c=getc(p))!=EOF){
str[i++] = c;
}
fclose(p);
str[i] = '\0';
for(i=0;i<strlen(str);i++){
if(str[i]=='/'&&str[i+1]=='/'){ //单行注释必然以换行符结束
while(str[i++]!='\n'){}
}else if(str[i]=='/'&&str[i+1]=='*'){ //多行注释
while(!(str[i]=='*'&&str[i+1]=='/')){i++;}
i+=2;
}else if(str[i]==' '&&str[i+1]==' '){ //多个空格,去除空格
while(str[i]==' '){i++;}
i--;
if(str1[j-1]!=' ')
str1[j++]=' ';
}else if(str[i]=='\n') { //换行符处理
if(str1[j-1]!=' ')
str1[j++]=' ';
}else if(str[i]==9){ //tab键处理
while(str[i]==9){
i++;
}
if(str1[j-1]!=' ')
str1[j++]=' ';
i--;
}else str1[j++] = str[i]; //其他字符处理
}
str1[j] = '\0';
if((p = fopen("result.txt","w"))==NULL){
printf("can not find it!");
return 0;
}else{
if(fputs(str1,p)!=0){
printf("预处理失败!");
}else
printf("预处理成功!");
}
fclose(p);
}
return 0;
}
//设置保留字
void set_reserve()
{
reserved_word[1]="return";
reserved_word[2]="def";
reserved_word[3]="if";
reserved_word[4]="else";
reserved_word[5]="while";
reserved_word[6]="return";
reserved_word[7]="char";
reserved_word[8]="for";
reserved_word[9]="and";
reserved_word[10]="or";
reserved_word[11]="int";
reserved_word[12]="bool";
}
//看这个字是不是字母
bool judge_word(char x)
{
if(x>='a' && x<='z' || x>='A' && x<='Z' ){
return true;
}
else return false;
}
//看这个字是不是数字
bool judge_number(char x)
{
if(x>='0' && x<='9'){
return true;
}
else return false;
}
//看这个字符是不是界符
bool judge_jiefu(char x)
{
if(x=='('||x==')'||x==','||x==';'||x=='{'||x=='}'){
return true;
}
else return false;
}
//加减乘
bool judge_yunsuanfu1(char x)
{
if(x=='+'||x=='-'||x=='*')
{
return true;
}
else return false;
}
//等于 赋值,大于小于 大于等于,小于等于,大于小于
bool judge_yunsuannfu2(char x)
{
if(x=='='|| x=='>'||x=='<'||x=='&'||x=='||'){
return true;
}
else return false;
}
//从文件里读一个单词
int scan(FILE *fp)
{
buffer=fgetc(fp); //读取一个字符
if(feof(fp)){ //检测结束符
flag=0;return 0;
}
else if(buffer==' ')
{
row++;
return 0;
}
else if(buffer=='\n')
{
row=1;
return 0;
}
else if(judge_word(buffer) || buffer=='_') //如果是字母开头或'_' 看关键字还是普通单词
{
word+=buffer;
row++;
while((buffer=fgetc(fp)) && (judge_word(buffer) || judge_number(buffer) || buffer=='_'))
{
word+=buffer;
row++;
}
if(feof(fp)){
flag=0;
return 2;
}
for(int i=1;i<=12;i++){
if(word==reserved_word[i]){
aa=fseek(fp,-1,SEEK_CUR);//如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。
return 1;
}
}
aa=fseek(fp,-1,SEEK_CUR);
return 2;
}
else if(judge_yunsuanfu1(buffer)) //开始是加减乘 一定是类型4
{
word+=buffer;
row++;
return 4;
}
else if(judge_jiefu(buffer)) //检验界符
{
word+=buffer;
row++;
return 5;
}
else if(judge_number(buffer)) //开始是数字就一定是数字
{
word+=buffer;
row++;
while((buffer=fgetc(fp)) && judge_number(buffer))
{
word+=buffer;
row++;
}
if(feof(fp)){
flag=0;
return 3;
}
aa=fseek(fp,-1,SEEK_CUR);
return 3;
}
else if(judge_yunsuannfu2(buffer)) //检验 <=、 >=、 <>、 == =、 <、>
{
row++;
word+=buffer;
if(buffer=='<') //为了检验题目中的<> <=
{
buffer=fgetc(fp);
if(buffer=='>' || buffer=='=')
{
word+=buffer;
row++;
return 4;
}
}
else{ //检验 >= ==
buffer=fgetc(fp);
if(buffer=='=')
{
word+=buffer;
row++;
return 4;
}
}
if(feof(fp)){
flag=0;
}
aa=fseek(fp,-1,SEEK_CUR);
return 4;
}
else if(buffer=='/') //首字符是/ 有可能是除号 也有可能是注释
{
row++;
word+=buffer;
buffer=fgetc(fp);
aa=fseek(fp,-1,SEEK_CUR);
return 4;
}
else {
word+=buffer;
row++;
return -1;
}
}
int main()
{
set_reserve();
process();
cout<<"open "<<"result.txt"<<endl;
flag=1;
FILE *fp;
if(!(fp=fopen("result.txt","r")))
{
cout<<"not found the file or other error "<<endl;
flag=0;
}
while(flag==1){
flag2=scan(fp); //反复调用函数提取单词并返回类型
if(flag2==1)
{
cout<<"type:1 reserved word "<<word<<endl;
word.erase(word.begin(),word.end()); //将word字符串清空
}
else if(flag2==2)
{
cout<<"type:2 identifier "<<word<<endl;
if(word.length()>20)
cout<<"ERROR Identifier length cannot exceed 20 characters"<<endl;
word.erase(word.begin(),word.end());
}
else if(flag2==3)
{
cout<<"type:3 positive number "<<word<<endl;
word.erase(word.begin(),word.end());
}
else if(flag2==4)
{
cout<<"type:4 unary_operator "<<word<<endl;
word.erase(word.begin(),word.end());
}
else if(flag2==5)
{
cout<<"type:5 double_operator "<<word<<endl;
word.erase(word.begin(),word.end());
}
else if(flag2==-1) //非法字符
{
cout<<"Illegal character "<<word<<endl;
word.erase(word.begin(),word.end());
}
}
int a=fclose(fp);
cout<<"press e to close"<<endl;
char end;
while(cin>>end && end!='e'){
cout<<"只有e可以关闭"<<endl;
}
return 0;
}
本次实验让我了解如何设计编制并调试词法分析程序,加深了我对词法分析器原理的理解。词法分析是编译的第一阶段。词法分析器的主要任务是读入源程序的输入字符,将它们组成词素,生成并输出一个词法单元序列,这个词法单元序列被输出到语法分析器进行语法分析。另外,由于词法分析器在编译器中负责读取源程序,因此除了识别词素之外,它还会完成一些其他任务,比如过滤掉源程序中的注释和空白,将编译器生成的错误消息与源程序的位置关联起来等。