一、 实验目的
设计并实现一个包含预处理功能的词法分析程序,加深对编译中词法分析过程的理解。
二、 实验要求
1、实现预处理功能
源程序中可能包含有对程序执行无意义的符号,要求将其剔除。
首先编制一个源程序的输入过程,从键盘、文件或文本框输入若干行语句,依次存入输入缓冲区(字符型数据);然后编制一个预处理子程序,去掉输入串中的回车符、换行符和跳格符等编辑性文字;把多个空白符合并为一个;去掉注释。
2、实现词法分析功能
输入:所给文法的源程序字符串。
输出:二元组(syn,token或sum)构成的序列。其中,
syn为单词种别码。
Token为存放的单词自身字符串。
Sum为整型常量。
具体实现时,可以将单词的二元组用结构进行处理。
3、待分析的C语言子集的词法
1)关键字
main if then while do static int double struct break else long switch case typedef char return const float short continue for void default sizeof do
所有的关键字都是小写。
2)运算符和界符
+ - * / : := < <> <= > >= = ; ( ) #
3)其他标记ID和NUM
通过以下正规式定义其他标记:
ID→letter(letter|digit)*
NUM→digit digit*
letter→a|…|z|A|…|Z
digit→0|…|9…
4)空格由空白、制表符和换行符组成
空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。
4、各种单词符号对应的种别码
表1 各种单词符号的种别码
单词符号 种别码 单词符号 种别码
main 1 ; 41
if 2 ( 42
then 3 ) 43
while 4 int 7
do 5 double 8
static 6 struct 9
ID 25 break 10
NUM 26 else 11
+ 27 long 12
- 28 switch 13
* 29 case 14
/ 30 typedef 15
: 31 char 16
:= 32 return 17
< 33 const 18
<> 34 float 19
<= 35 short 20
> 36 continue 21
>= 37 for 22
= 38 void 23
default 39 sizeof 24
do 40 # 0
5、 词法分析程序的主要算法思想
算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到的单词符号的第一个字符的种类,拼出相应的单词符号。
6、实验过程
预处理子程序,功能目的:去掉输入串中的回车符、换行符和制表符等编辑性文字;把多个空格符合并为一个;并去掉注释。
void procedure1 (char aaa[],int num){
FILE *out;
int i;
char bbb[10000];
if((out=fopen("bbb.txt","w"))==NULL){
printf("无法打开此文件\n");
exit(0);
}
for(i=0;i<=num;i++)
{
//遍历文件过程中,去掉回车符、换行符和跳格符等编辑性文字;将两个以上的空格符合并为一个。
if( aaa[i] == '\n' || aaa[i] == '\t'){
continue;
}
if( aaa[i] == ' '){
fputc(aaa[i],out);
i++; //先将空格符读入文件中,后来就不再考虑空格符是否读到out文件中
while( aaa[i] == ' '){
i++;
} //从文件中读取的字符只要是空格符就一直循环下去
//读取到最后一个不是空格符的字符时跳出循环,
if( aaa[i] == '\n' || aaa[i] == '\t'){
continue;
}
if( aaa[i] == '/'){
i++;
if(aaa[i] == '/'){
i++;
while(aaa[i]!= '\n'){
i++;
} //读到换行符时结束
}else if(aaa[i]=='*'){
i++;
while(aaa[i++]!= '*'){
i++;
}
i++; //将*/中的/读走
}
continue;
}
fputc(aaa[i],out);
continue;
}
if( aaa[i] == '/'){
i++;
if(aaa[i] == '/'){
i++;
while(aaa[i]!= '\n'){
i++;
} //读到换行符时结束
}else if(aaa[i]=='*'){
i++;
while(aaa[i++]!= '*'){
i++;
}
i++; //将*/中的/读走
}
continue;
}
fputc(aaa[i],out);
//将指针s指向的字符放入bbb.txt文件中
}
}
词法分析程序,功能目的:从字符串表示的源程序中识别出具有独立意义的单词符号。
void procedure2(){
//指针d读出字符串存入token,整形常量存入sum(字符串、整形常量以空格符为间隔)
FILE *out,*in1;
char bbb[10000];
char ch;
int num=0;
int flag=1;
if((out=fopen("bbb.txt","r"))==NULL){
printf("不能打开bbb.txt文件夹\n");
exit(0);
}
if((in1=fopen("ccc.txt","w"))==NULL){
printf("不能打开ccc.txt文件夹\n");
exit(0);
}
ch=fgetc(out);
bbb[0]=ch;
while((ch=fgetc(out))!=EOF){
num++;
bbb[num]=ch;
}
num++;
bbb[num]='$';
num=0;
while(bbb[num]!='$')
{
//开始分析新字符时,将cot重新指向token第一个位置
int cot=0;
//过滤空格
while(bbb[num]==' '){
num++;
}
for(int j=0;j<20;j++)
{
token[j]='\0';
}
if(isLetter(bbb[num])){ //第一个字符是字母
token[cot]=bbb[num];
num++;
cot++;
while(isLetter(bbb[num])||isDigit(bbb[num])){
token[cot]=bbb[num];
cot++;
num++; //为下一次做准备
}
token[cot]='\0';
num--;
//判断截取的字符串是关键字还是标识符
syn=panduan(token);
if(syn==25){
//标识符
for(int i=0;i<300;i++)
{
if(strcmp(biaoshifu[i],token)==0){
break; //标识符已在表中
}
if(strcmp(biaoshifu[i],"")==0){
strcpy(biaoshifu[i],token);
}
}
/*printf("-----------");
printf("%s\n",token);
printf("-----------");*/
printf("(标识符 ,%s)\n",token);
fprintf(in1,"(标识符 ,%s)\n",token);
}else{ //关键字
printf("(%s ,%d)\n",token,syn);
fprintf(in1,"(%s ,%d)\n",token,syn);
}
}else if(isDigit(bbb[num])){ //第一个字符是数字
//取出单词
token[cot]=bbb[num];
cot++;
num++;
while(isLetter(bbb[num])||isDigit(bbb[num]))
{
token[cot]=bbb[num];
cot++;
num++; //为下一次做准备
}
token[cot]='\0';
num--;
for(int i=0;i'){
printf("(<> ,34)\n");
fprintf(in1,"(<> ,34)\n");
}else if(bbb[num]=='='){
printf("(<= ,35)\n");
fprintf(in1,"(<= ,35)\n");
}else {
printf("(< ,33)\n");
fprintf(in1,"(< ,33)\n");
num--;
}
}else if(bbb[num]=='>'){
num++;
if(bbb[num]=='='){
printf("(>= ,37)\n");
fprintf(in1,"(>= ,37)\n");
}else {
printf("(> ,36)\n");
fprintf(in1,"(> ,36)\n");
num--;
}
}else if(bbb[num]=='='){
printf("(= ,38)\n");
fprintf(in1,"(= ,38)\n");
}else if(bbb[num]==';'){
printf("(; ,41)\n");
fprintf(in1,"(; ,41)\n");
}else if(bbb[num]=='('){
printf("( ( ,42)\n");
fprintf(in1,"( ( ,42)\n");
}else if(bbb[num]==')'){
printf("( ) ,43)\n");
fprintf(in1,"( ) ,43)\n");
}else if(bbb[num]=='#'){
printf("(# ,0)\n");
fprintf(in1,"(# ,0)\n");
}
num++; //为下一次做准备
}
//判断token是不是关键字、标识符、界符。如果是,输出<关键字/运算符/界符,种别码>
//如果不是,输出<标示符/数字,字符串/整形常量>
}
程序入口,主函数
int main(int argc, char** argv) {
char aaa[10000];
FILE *in;
int num=0;
char ch;
if((in=fopen("aaa.txt","r"))==NULL){
printf("无法打开此文件\n");
exit(0);
}
aaa[0]=fgetc(in);
while((ch=fgetc(in))!=EOF){
num++;
aaa[num]=ch;
}
fclose(in);//以$作为数组的结束符
procedure1(aaa,num);
procedure2();
}
实验过程中涉及到的文档aaa.txt(源程序文本),bbb.txt(预处理过的源程序文本),ccc.txt(词法分析得到的单词字符)
7、实验结果
预处理过的文档内容:
分离出的单词符号,输出结果很长,就放一张图: