词法分析程序构造原理与实现技术编译原理实验报告.docx
《词法分析程序构造原理与实现技术编译原理实验报告.docx》由会员分享,可在线阅读,更多相关《词法分析程序构造原理与实现技术编译原理实验报告.docx(23页珍藏版)》请在冰豆网上搜索。
词法分析程序构造原理与实现技术编译原理实验报告
编译原理实验
词法分析程序构造原理与实现技术
目录
引言3
一、实验目的3
二、实验环境3
三、实验内容3
四、实验要求4
五、程序功能描述4
5.1输入输出4
5.2大小写不敏感5
5.3识别大多数常用程序字符5
5.4注释的处理5
5.5错误检查和处理5
六、主要数据结构6
七、程序结构描述6
7.1各单词符号的类别编码6
7.2程序总体以及局部执行图6
7.3设计方法8
7.4函数定义及函数之间的调用关系8
八、程序测试8
代码附录12
引言
本课程开展研究性教学的目的是,在教师的引导下以问题回朔与思维启发的方式,使学生在不断的探究过程中掌握编译程序设计和构造的基本原理和实现技术,启迪学生的抽象思维、激发学生的学习兴趣、培养学生的探究精神和专业素养,从而提高学生发现问题、分析问题和解决问题的能力。
一、实验目的
1.解决正则文法到状态转换图的转化问题
2.掌握正则文法及其状态转换图的基本概念,正则表达式及有限自动机的基本概念
3.深入理解理论对实践的指导作用:
基本原理、实现技术和方法的正确运用
4.掌握正规文法构造相应的状态转换图的基本方法和正则表达式构造有限自动机的基本方法及不确定有限自动机确定化的基本方法
5.完成词法分析程序的设计与编写
二、实验环境
软件:
操作系统:
WINDOWS8
应用软件:
VC6.0
三、实验内容
1、教学安排内容:
完成以下正则文法所描述的Pascal语言子集单词符号的词法分析程序。
<标识符>→字母︱<标识符>字母︱<标识符>数字
<无符号整数>→数字︱<无符号整数>数字
<单字符分界符>→+︱-︱*︱;︱(︱)
<双字符分界符>→<大于>=︱<小于>=︱<小于>>︱<冒号>=︱<斜竖>*
<小于>→<<等于>→=<大于>→><冒号>→:
<斜竖>→/
该语言的保留字:
beginendifthenelsefordowhileandornot
2、在此基础上,我自己新完善了一下词法分析处理:
1)新增保留字:
returnbreakmain
2)扩充可识别的标识符文法:
<标识符>→<标识符><下划线>字母︱<标识符><下划线>数字
3)新增可识别的经常使用的符号:
如'︱"︱==︱!
=︱\n(\t)(\r)︱%s(%d)(%f)等,以及数组(形如a[])还有程序经常出现的{},<>等
四、实验要求
1.给出各单词符号的类别编码;
2.词法分析程序应能发现输入串中的错误;
3.词法分析作为单独一遍编写,词法分析结果为二元式序列组成的中间文件;
4.设计两个测试用例(尽可能完备),并给出测试结果。
五、程序功能描述
5.1输入输出
输入:
将事先准备好的代码录入txt中,程序将改*.txt进行词法分析
输出:
在命令行中以二元组(类别码,TOKEN)显示
5.2大小写不敏感
将纯字母组合中的大写字母转换成小写后,在保留字数组中查找是否是系统保留字
5.3识别大多数常用程序字符
能够识别C语言中常见的\n\r\t,%s%d%f,单引号,双引号,逻辑运算符以及形如a[]的数组的符号等。
5.4注释的处理
1.当输入文件中出现了/*的字符串之后,程序会自动忽略之后的输入字符串,直到遇*/,但程序会输出/*
2.当输入文件中出现了//的字符串之后,程序会自动忽略之后的输入字符串,直到遇回车字符后,则判断为注释结束,但程序会输出//
5.5错误检查和处理
当输入串为未定义的字符时,在输出文件中显示“Error!
”,如输入¥
六、主要数据结构
主要是利用数组,进行保留字的查询与将符合某个正则文法的字符归结到一起。
七、程序结构描述
7.1各单词符号的类别编码
单词符号
类别码
单词符号
类别码
begin
1
:
=
23
end
2
+
24
if
3
-
25
then
4
*
26
else
5
/
27
for
6
;
28
do
7
(
29
while
8
)
30
and
9
{
31
or
10
}
32
not
11
数组(形如a[])
33
return
12
#
34
break
13
.
35
main
14
36
ID标识符
15
形如\n,
37
INT整型
16
形如%s
38
<
17
"
39
<=
18
!
40
=
19
!
=
41
<>
20
==
42
>
21
’
43
>=
22
//|/*
44|45
7.2程序总体以及局部执行图
总体流程图:
词法分析流程图:
7.3设计方法
1.预定义好各单词符号的类别,包括保留字
2.用数组来存储识别过程中的单词字符
3.循环一个一个字符的读取与分析,若开头是字母,则判断为标识符,并将其转换为小写,然后读取完后,进行保留字比较,若是,则输出保留字的二元组;不是则输出标识符的二元组
4.开头不是字母的而是数字的,则继续读取,直到数字全读完,输出类别是整型的二元组
5.若不属于以上2中类型,则转到其他字符的判别,查找宏定义中的字符,找到则输出其二元组,没有找到,则进行出错输出。
7.4函数定义及函数之间的调用关系
main函数中,调用fopen函数,将文本文件打开,在调用scanner_example函数进行词法分析,在分析过程中,调用lookup函数进行保留字查找,用out函数进行二元组的输出,用report_error函数来进行出错检查
八、程序测试
测试文件1:
#include
charTOKEN[20];
charTOKEN1[20];/*sjsj*/
if(strcmp(temp,keyword[i])==0)//hksk
{
x=i+1;
returnx;
break;
}
¥
voidmain()
{
FILE*fp;
charfilename[10];/*sdsfsfs*/
while
(1)
{
printf("\n\r\t");
scanf("%s",filename);
fp=fopen(filename,"r");//122344
scanner_example(fp);
fclose(fp);
}
}
运行截图:
测试文件2:
#include
#include
/*======================宏定义部分=========================*/
#defineID15//标识符
#defineINT16//整型
#defineLT17//"<"
voidmain()
{
FILE*fp;
charfilename[10];
while
(1)
{
printf("\n");
scanf("%s",filename);
fp=fopen(filename,"r");/*以只读的方式打开文本文件*/
scanner_example(fp);
fclose(fp);
}
}
截图:
代码附录
/*======================头文件部分=========================*/
#include
#include
#include
#include
#include
/*======================宏定义部分=========================*/
#defineID15//标识符
#defineINT16//整型
#defineLT17//"<"
#defineLE18//"<="
#defineEQ19//"="
#defineNE20//"<>"
#defineGT21//">"
#defineGE22//">="
#defineDE23//":
="
#defineADD24//"+"
#defineSUB25//"-"
#defineMUL26//"*"
#defineDIV27//"/"
#defineSE28//";"
#defineBL29//"("
#defineBR30//")"
#defineHL31//"{"
#defineHR32//"}"
#defineSZ33//"形如a[]"
#defineTS34//"#"
#definePO35//"."
#defineCO36//","
#defineTX37//"形如\n"
#defineTx38//"形如%s"
#defineQU39//"""
#defineTH40//"!
"
#defineBD41//"!
="
#defineDY42//"=="
#defineSQ43//"'"
#defineX44//"//"
#defineXX45//"/*"
charTOKEN[20];
charTOKEN1[20];
char*keyword[14]={"begin","end","if","then","else","for","do","while","and","or","not","return","break","main"};
/*======================函数声明部分=======================*/
intlookup(char*);
voidout(int,char*);
voidscanner_example(FILE*);
voidreport_error(void);
/*======================函数实现部分=======================*/
/*----------------------查找保留字------------------------*/
intlookup(char*temp)
{
inti,x=15;
for(i=0;i<14;i++)
{
if(strcmp(temp,keyword[i])==0)
{
x=i+1;
returnx;
}
}
returnx;
}
/*----------------------输出二元式------------------------*/
voidout(inta,char*temp)
{
printf("(%d,%s)\n",a,temp);
}
/*----------------------报错-----------------------------*/
voidreport_error(void)
{
printf("Error!
\n");
}
/*----------------------词法分析-------------------------*/
voidscanner_example(FILE*fp)
{
charch;
charstr[3];
inti,c;
while
(1)
{
ch=fgetc(fp);
if(ch==EOF)
{
break;
}
elseif(ch==''||ch=='')/*跳过程序中的空格字符*/
{
//
}
else
{
if(isalpha(ch))/*开头是字母的统一归到标识符或者保留字一类*/
{
TOKEN[0]=tolower(ch);/*将大写字符转换成小写*/
TOKEN1[0]=ch;
ch=fgetc(fp);
i=1;
while(isalnum(ch))/*将标识符完整的读到数组TOKEN中*/
{
TOKEN[i]=tolower(ch);/*将大写字符转换成小写*/
TOKEN1[i]=ch;
i++;
ch=fgetc(fp);
}
TOKEN[i]='\0';
TOKEN1[i]='\0';
if(!
feof(fp))
{fseek(fp,-1,1);}
c=lookup(TOKEN);
if(c==15)
{
while
(1)
{
ch=fgetc(fp);
if(ch==EOF)
{out(ID,TOKEN1);break;}
elseif(ch=='')
{out(ID,TOKEN1);break;}
elseif(ch=='[')/*识别数组*/
{
TOKEN1[i]=ch;
ch=fgetc(fp);
i++;
while(isalnum(ch))
{
TOKEN1[i]=ch;
i++;
ch=fgetc(fp);
}
if(ch==']')
{
TOKEN1[i]=ch;
out(SZ,TOKEN1);
break;
}
}
elseif(ch=='_')/*识别带有下标的标识符*/
{
TOKEN1[i]=ch;
ch=fgetc(fp);
i++;
while(isalnum(ch))
{
TOKEN1[i]=ch;
i++;
ch=fgetc(fp);
}
TOKEN1[i]='\0';
fseek(fp,-1,1);
out(ID,TOKEN1);
break;
}
else
{out(ID,TOKEN1);break;}
}
}
else
{
out(c,TOKEN1);
}
}
else
{
if(isdigit(ch))/*识别纯数字*/
{
TOKEN[0]=ch;
ch=fgetc(fp);
i=1;
while(isdigit(ch))
{
TOKEN[i]=ch;i++;
ch=fgetc(fp);
}
TOKEN[i]='\0';
fseek(fp,-1,1);
out(INT,TOKEN);
}
else
{
switch(ch)/*识别单字符分界符、双字符分界符以及一些程序中经常用到的逻辑运算符,注释(注释--统一忽略)等*/
{
case'+':
out(ADD,"+");break;
case'-':
out(SUB,"-");break;
case'*':
out(MUL,"*");break;
case';':
out(SE,";");break;
case'(':
out(BL,"(");break;
case')':
out(BR,")");break;
case'{':
out(HL,"{");break;
case'}':
out(HR,"}");break;
case'#':
out(TS,"#");break;
case'.':
out(PO,".");break;
case',':
out(CO,",");break;
case'\"':
out(QU,"\"");break;
case'\'':
out(SQ,"\'");break;
case'!
':
ch=fgetc(fp);/*读到"!
"的时候,再读一个字符,如果是"=",则判断为逻辑运算符"!
=",否则判断为普通的"!
"*/
if(ch=='=')out(BD,"!
=");
else
{
fseek(fp,-1,1);/*多读的那一个字符要退回去,避免将下一个读错*/
out(TH,"!
");
}
break;
case'\\':
ch=fgetc(fp);/*读到"\"的时候,再读一个字符,如果是"n或t或r",则判断为回车、换行或者制表"\n或\r或\t"*/
str[0]='\\';
if(ch=='n'||ch=='t'||ch=='r')
str[1]=ch;str[2]='\0';out(TX,str);break;
case'%':
ch=fgetc(fp);/*读到"%"的时候,再读一个字符,如果是"s或d或f",则判断为输出的类型"%s或%d或%f"*/
str[0]='%';
if(ch=='s'||ch=='d'||ch=='f')
str[1]=ch;str[2]='\0';out(Tx,str);break;
case'/':
ch=fgetc(fp);/*读到"/"的时候,再读一个字符,如果是"*",则判断为注释,跳过,一直读到结束为止;如果是"/",则也为注释,读到回车就结束*/
if(ch=='*')
{
out(XX,"/*");
while
(1)
{
ch=fgetc(fp);
if(ch=='*')
{
ch=fgetc(fp);
if(ch=='/')
{break;}
}
}
}
elseif(ch=='/')
{
out(X,"//");
while
(1)
{
ch=fgetc(fp);
if(ch=='\n')
{break;}
}
}
else
{
fseek(fp,-1,1);/*多读的那一个字符要退回去,避免将下一个读错*/
out(DIV,"/");
}
break;
case'<':
ch=fgetc(fp);/*读到"<"的时候,再读一个字符,如果是"=",则判断为关系运算符"<=",否则判断为"<"*/
if(ch=='=')out(LE,"<=");
elseif(ch=='>')out(NE,"<>");
else
{
fseek(fp,-1,1);/*多读的那一个字符要退回去,避免将下一个读错*/
out(LT,"<");
}
break;
case'=':
ch=fgetc(fp);/*读到"="的时候,再读一个字符,如果是"=",则判断为逻辑运算符"==",否则判断为"="*/
if(ch=='=')out(DY,"==");
else
{
fseek(fp,-1,1);
out(EQ,"=");
}
break;
case'>':
ch=fgetc(fp);/*读到">"的时候,再读一个字符,如果是"=",则判断为关系运算符">=",否则判断为">"*/
if(ch=='=')out(GE,">=");
else
{
fseek(fp,-1,1);/*多读的那一个字符要退回去,避免将下一个读错*/
out(GT,">");
}
break;
case':
':
ch=fgetc(fp);/*读到":
"的时候,再读一个字符,如果是"=",则判断为定义":
=",否则判断为":
"*/
if(ch=='=')out(DE,":
=");
break;
case'\n':
break;
default:
report_error();
break;
}
}
}
}
}
return;
}
voidmain()
{
FILE*fp;
charfilename[10];
while
(1)
{
printf("\n");
printf("请输入要打开的文件名:
");
scanf("%s",filename);
fp=fopen(filename,"r");/*以只读的方式打开文本文件*/
scanner_example(fp);
fclose(fp);
}
}