1.实验要求
输入为一个以类C语言编写的源程序
输出为一组二元组序列构成的文本文件,一行为一个二元组,二元组中间以逗号隔开
实验报告上要求附上DFA
2.语言说明:
保留字:unsigned、break、return、void、case、float、char、for、while、continue、if、default、do、int、switch、double、long、else
运算符:+,-,*,/,>,>=,<,<=,==,!=,&&,||,!
界限符:{ }( ); ,
常量:十进制无符号数
标识符:以字母或下划线开始,后面跟上字母或数字
3. 实验原理
词法分析是编译的第一阶段。词法分析器的主要任务是读入源程序的输入字符,将它们组成词素,生成并输出一个词法单元序列,这个词法单元序列被输出到语法分析器进行语法分析。另外,由于词法分析器在编译器中负责读取源程序,因此除了识别词素之外,它还会完成一些其他任务,比如过滤掉源程序中的注释和空白,将编译器生成的错误消息与源程序的位置关联起来等。词法分析器的作用如下:
读入源程序的输入字符,将它们组成词素,生成并输出一个词法单元序列;
过滤掉源程序中的注释和空白;
将编译器生成的错误消息与源程序的位置关联起来;
4.DFA设计:
5.实验代码
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<iomanip>
#include<ctype.h>
#include<fstream>
using namespace std;
typedef pair<int,int> mp;
const string key_word[]={"unsigned","break","return","void","case","float","char","for","while","continue","if","default","do","int","switch","double","long","else"};//保留字
const string operators[]={"+","-","*","/",">",">=","<","<=","==","!=","&&","||","!"};//运算符
const char jiefu[]={',',';','(',')','{','}'};//界符
map<string,mp> flag_table;//标识符表等
map<string,mp>num_table;//常量表
map<string,mp>str_table;//字符串表
map<string,mp>head_table;//头文件表
map<string,mp>char_table;//字符表
map<char,mp>fenjiefu_table;//分界符表
int keymark[40],operatormark[40];
int isjiefu(char ch)
{
for(int i=0;i<5;i++)
if(ch==jiefu[i])
return 1;
return 0;
}
//判断是不是关键字
int iskey(string ch)
{
int i;
for(i=0;i<17;i++)
if(key_word[i]==ch)
return i;
return -1;
}
int isope(string ch)
{
int i;
for(i=0;i<12;i++)
if(operators[i]==ch)
return i;
return -1;
}
int ischar(char ch)
{
if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')
return 1;
return -1;
}
int isnumber(char ch)
{
if(ch>='0'&&ch<='9')
return 1;
return -1;
}
//获得关键字或者标识符
void get_keyflag(char* ptr,FILE* f)
{
string token;
token+=*ptr;
while(1)
{
*(++ptr)=fgetc(f);
if(ischar(*ptr)==-1&&isnumber(*ptr)==-1&&*ptr!='_')
break;
token+=*ptr;
}
ungetc(*ptr,f);
//*ptr='\0';
int h=iskey(token);
if(h>=0)//是关键字
{
if(!keymark[h])
keymark[h]=1;
cout<<token<<" , "<<"保留字"<<endl;
}
else//标识符
{
mp tmp;
if(flag_table.find(token)==flag_table.end())//崭新的标记符
{
tmp=make_pair(1,flag_table.size()+1);
flag_table[token]=tmp;
}
else
{
map<string,mp>::iterator it;
it=flag_table.find(token);
tmp=it->second;
}
cout<<token<<" , "<<"标识符"<<endl;
}
}
//获取数字
void get_num(char* ptr,FILE* f)
{
string token;
token+=*ptr;
while(1)
{
*(++ptr)=fgetc(f);
if(isnumber(*ptr)!=1)
break;
token+=*ptr;
}
ungetc(*ptr, f);
mp tmp;
if(num_table.find(token)==num_table.end())//新的数字
{
tmp=make_pair(2,num_table.size()+1);//2代表着数字
num_table[token]=tmp;
}
else
{
map<string,mp>::iterator it;
it=num_table.find(token);
tmp=it->second;
}
cout<<token<<" , "<<"常量"<<">"<<endl;
}
//获得字符串
void get_string(char* ptr,FILE* f)
{
string token;
*(++ptr)=fgetc(f);
token+=*(ptr);
while(1)
{
*(++ptr)=fgetc(f);
if(feof(f)||*ptr=='"')
break;
token+=*(ptr);
}
if(*(ptr)=='"')
{
map<string,mp>::iterator it;
it=str_table.find(token);
mp tmp;
if(it==str_table.end())//新的字符串
{
tmp=make_pair(3,str_table.size()+1);
str_table[token]=tmp;
}
else
{tmp=it->second;}
cout<<token<<" , "<<"标识符"<<endl;
}
//找不到匹配的字符串
else
{
cout<<"程序错误!"<<endl;
}
}
void double_operator(char* ptr,FILE* f)
{
string token,tmp;
token+=*ptr;
tmp+=*ptr;
*(++ptr)=fgetc(f);
token+=*ptr;
int p=isope(token);
if(p>=0)
{
operatormark[p]=1;
cout<<token<<" , "<<"运算符"<<endl;
}
else
{
ungetc(*ptr,f);
p=isope(tmp);
operatormark[p]=1;
cout<<tmp<<" , "<<"运算符"<<endl;
}
}
void single_operator(char* ptr,FILE* f)
{
string token;
token+=*ptr;
int index=isope(token);
if(index>=0)
{
operatormark[index]=1;
cout<<token<<" , "<<"运算符"<<endl;
}
else
{
if(*ptr<0||*ptr>127)
cout<<"输入的不是ascii码"<<endl;
}
}
void acehandle(char* ptr,FILE* f)
{
string token;
token+=*ptr;
while(1)
{
ptr++;
*ptr=fgetc(f);
if(*ptr==' '||*ptr=='"'||*ptr=='<')
break;
token+=*ptr;
}
if(*ptr=='<'||*ptr=='"')
ungetc(*ptr,f);
if(token=="#define")
{
cout<<token<<","<<"宏定义"<<endl;
}
if(token=="#include")
{
cout<<setw(5)<<"<"<<setw(10)<<token<<setw(20)<<"预处理"<<setw(20)<<">"<<endl;
*ptr=fgetc(f);
cout<<setw(5)<<"<"<<setw(10)<<*ptr<<setw(20)<<"分界符"<<setw(20)<<">"<<endl;
string tmp;
*ptr=fgetc(f);
tmp+=*ptr;
while(1)
{
*(++ptr)=fgetc(f);
if(*ptr=='>'||*ptr=='"')
break;
tmp+=*ptr;
}
mp p;
if(head_table.find(tmp)==head_table.end())
{
p=make_pair(4,head_table.size()+1);
head_table[tmp]=p;
}
else
{
map<string,mp>::iterator it;
it=head_table.find(tmp);
p=it->second;
}
cout<<setw(5)<<"<"<<setw(10)<<tmp<<setw(10)<<p.first<<setw(10)<<"头文件"<<setw(10)<<p.second<<">"<<endl;
cout<<setw(5)<<"<"<<setw(10)<<*ptr<<setw(20)<<"分界符"<<setw(20)<<">"<<endl;
}
}
void handlechar(char ch,FILE* f)
{
char* ptr=new char[1000];
*ptr=ch;
if(ischar(*ptr)==1)//以字母开头的--关键字或者标识符
{
get_keyflag(ptr,f);}
else if(isnumber(*ptr)==1)
{
get_num(ptr,f);}
else if(*ptr=='#')
{
acehandle(ptr,f);}
else if(*ptr=='"')
{
get_string(ptr,f);}
else if(*ptr=='+'||*ptr=='-'||*ptr=='*'||*ptr=='/'||*ptr=='='||*ptr=='%'||*ptr=='>'||*ptr=='<')
{
double_operator(ptr,f);}
else if(isjiefu(*ptr)==1)
{
mp tmp;
map<char,mp>::iterator it;
if(fenjiefu_table.find(*ptr)==fenjiefu_table.end())
{
tmp=make_pair(6,fenjiefu_table.size()+1);
fenjiefu_table[*ptr]=tmp;}
else
{
it=fenjiefu_table.find(*ptr);
tmp=it->second;}
cout<<*ptr<<" , "<<"界符"<<endl;
}
else
{
single_operator(ptr,f);}
}
int main()
{
FILE* f;
f=fopen("input.txt","r");
if(f==NULL)
{
cout<<"读入文件错误"<<endl;
return 0;}
else
{
char ch=fgetc(f);
while(!feof(f))
{
if(ch!=' '&&ch!='\n'&&ch!='\t')
handlechar(ch,f);
ch=fgetc(f);}
}
fclose(f);
return 0;
}
输入的类C词法
char word[10];
char pro[100][100] = { "PROGRAM",
"BEGIN", "END", "VAR", "INTEGER", "WHILE", "IF", "THEN",
"ELSE", "DO", "PROCEDURE" ,
"char","int","if","else","var"
,"return","break","do","while","for","double","float","short"};
int n = 0;
word[n++] = a[i++];
while ((a[i] >= 'A'&&a[i] <= 'Z') || (a
[i] >= '0' && a[i] <= '9')||(a[i]>='a'&&a[i]<='z'))
{
word[n++] = a[i++];
}
word[n] = '\0';
i--;
程序运行输出结果:
6.分析讨论:
(1)遇见问题:二目运算符无法识别 ,导致拆分识别为两个单目运算符;第二个问题就是如果识别的词法带有注释,会报错。
(2)解决方法: 修改识别运算符代码部分加入双目运算符的识别
(3)后续改进思路:一个不方便的地方就是没能实现通过输入文件名读取文件的数据,每次读入新的文件仍需要修改代码。