编译原理 词法分析.docx
《编译原理 词法分析.docx》由会员分享,可在线阅读,更多相关《编译原理 词法分析.docx(22页珍藏版)》请在冰豆网上搜索。
编译原理词法分析
实验报告
学院(系)名称:
计算机与通信工程学院
姓名
建坤
学号
专业
计算机科学与技术
班级
2010级2班
实验项目
实验一:
词法分析
课程名称
编译原理
课程代码
0668056
实验时间
2013年3月22日第5~8节
2013年3月26日第1、2节
实验地点
软件实验室7-215
批改意见
成绩
教师签字:
实验内容:
实现标准C语言词法分析器
实验目的:
1.掌握程序设计语言词法分析的设计方法;
2.掌握DFA的设计与使用方法;
3.掌握正规式到有限自动机的构造方法;
实验要求:
1.单词种别编码要求
基本字、运算符、界符:
一符一种;
标识符:
统一为一种;
常量:
按类型编码;
2.词法分析工作过程中建立符号表、常量表,并以文本文件形式输出;
3.词法分析的最后结果以文本文件形式输出;
4.完成对所设计词法分析器的功能测试,并给出测试数据和实验结果;
5.为增加程序可读性,请在程序中进行适当注释说明;
6.整理上机步骤,总结经验和体会;
7.认真完成并按时提交实验报告。
【实验过程记录(源程序、测试用例、测试结果及心得体会等)】
符号对照:
种类
标识
正则表达式
关键字
KEY
[_A-Za-z][0-9_A-Za-z]
标识符
ID
[_A-Za-z][0-9_A-Za-z]
整型常量
INT
[0-9]+
浮点常量
FLOAT
[0-9](\.[0-9]+)?
(E[+-]?
[0-9]+)?
界符
DELIM
[,;\[\]\{\}\(\)]
运算符
OPT
[!
%\^&\*\+\-<>?
/]|&&|<=|>=|==|\|\|
字符常量
CHAR
^\'$\'
字符串常量
STR
^\"$\"
源程序:
#include
#include
#include
#include
#include
#defineKEY0
#defineID1
#defineINT2
#defineFLOAT3
#defineCHAR4
#defineSTR5
#defineDELIM6
#defineOPT7
char*TYPE[]={"KEY","ID","INT","FLOAT","CHAR","STR","DELIM","OPT"};
charfilename[256];//要处理的文件名
longlength;//文件长度
char*key[]={"auto","break","case","char","const","continue",
"default","do","double","else","enum","extern",
"float","for","goto","if","int","long",
"register","return","short","signed","sizeof","static",
"struct","switch","typedef","union","unsigned","void",
"volatile","while",NULL};
constintKEYCOUNT=32;
char*source;//源码的内存缓冲区
longpos=0;//指向源码的指针
longline=1;//行号
longcolumn=0;//列号
chartoken[1024];//临时存放标识符
voiderror(constchar*error,...){
va_listarglist;
va_start(arglist,error);
vfprintf(stderr,error,arglist);
va_end(arglist);
exit(EXIT_FAILURE);
}
voidwarning(constchar*error,...){
va_listarglist;
va_start(arglist,error);
vfprintf(stderr,error,arglist);
va_end(arglist);
}
//判断是否为分隔符,是的话返回1,否则返回0
intisdelim(constcharch){
switch(ch){
case'(':
case')':
case'[':
case']':
case'{':
case'}':
case',':
case';':
case':
':
return1;
}
return0;
}
intisoperator(constcharch){
switch(ch){
case'+':
case'-':
case'*':
case'/':
case'<':
case'>':
case'?
':
case'=':
case'&':
case'!
':
case'~':
case'%':
case'|':
case'^':
case'.':
return1;
}
return0;
}
intiskey(constchar*str){
for(inti=0;iif(strcmp(key[i],str)==0)
returni+1;
}
return0;
}
//读入源码的一个字符
charreadSource(){
if(poscharch=source[pos++];
if(ch=='\n'){
line++;
column=0;
}
else
column++;
returnch;
}
else{
error("未预料的EOF\n");
return0;
}
}
//回退源码指针
voidback(){
if(pos==0){
error("errorinback():
posnowis0\n");
}
else
pos--;
}
longgetFileLength(FILE*fp){
longpos=ftell(fp);
longlength;
fseek(fp,0,SEEK_END);
length=ftell(fp);
fseek(fp,pos,SEEK_SET);
returnlength;
}
voidpreprocess(){
for(charch;ch=readSource();){
if(ch=='#'){
inti=0;
token[i++]=ch;
while((ch=readSource())!
='\n')
token[i++]=ch;
token[i]='\0';
if(token[0]=='#')
printf("%d\t%s\n",line,token);
}
else{
if(ch!
='\n')
back();
break;
}
}
}
voidgetOperator(charch){
token[0]=ch;
ch=readSource();
token[1]=ch;
token[2]='\0';
switch(token[0]){
case'<':
case'>':
if(token[0]==token[1]||token[1]=='=')
break;
case'|':
case'&':
case'+':
case'-':
case'=':
if(token[0]==token[1])
break;
case'!
':
if(token[1]=='=')
break;
default:
token[1]='\0';
back();
break;
}
printf("%ld\t%s\t%s\n",line,TYPE[OPT],token);
}
voidcomment(){
charch=readSource();
if(ch=='*'){
while
(1){
ch=readSource();
if(ch=='*'){
ch=readSource();
if(ch=='/')
break;
}
}
}
elseif(ch=='/'){
while
(1){
ch=readSource();
if(ch=='\n')
break;
}
}
else{
back();
getOperator('/');
}
}
voidgetChar(charch){
inti=0;
for(;(ch=readSource())!
='\'';i++){
token[i]=ch;
if(ch=='\\'){
ch=readSource();
token[++i]=ch;
}
}
token[i]='\0';
if(token[0]!
='\\'&&strlen(token)>1){
error("字符常量字符多余1个\n");
}
printf("%ld\t%s\t\'%s\'\n",line,TYPE[CHAR],token);
}
voidgetString(charch){
inti=0;
for(;(ch=readSource())!
='"';i++){
token[i]=ch;
if(ch=='\\'){
ch=readSource();
if(ch=='\n')
i--;
else
token[++i]=ch;
}
}
token[i]='\0';
printf("%ld\t%s\t\"%s\"\n",line,TYPE[STR],token);
}
voidgetIdentify(charch){
inti=1;
token[0]=ch;
for(;!
isspace(ch=readSource());i++){
if(isalpha(ch)||isdigit(ch)||ch=='_'){
token[i]=ch;
}
elseif(isoperator(ch)||isdelim(ch)||ch=='.'){
back();
break;
}
else{
error("非法标识符字符\n",filename,line,column);
}
}
token[i++]='\0';
if(iskey(token))
printf("%ld\t%s\t%s\n",line,TYPE[KEY],token);
else
printf("%ld\t%s\t%s\n",line,TYPE[ID],token);
}
voidgetNum(charch){
inti=0;
inthasDot=0;
inttype=0;//0isint1isfloat
intafterE=0;
if(ch=='.'){
hasDot=1;
type=1;
}
token[0]=ch;
for(i=1;i<256;i++){
ch=readSource();
if(token[0]=='.'&&i==1){
if(!
isdigit(ch)){
getOperator('.');
back();
return;
}
}
if(ch=='.'){
if(!
hasDot){
hasDot=1;
type=1;
}
else
error("数字中小数点多于1个\n");
}
elseif(ch=='E'||ch=='e'){
afterE=1;
type=1;
}
elseif(ch=='+'||ch=='-'){
if(afterE){
afterE=0;
}
else{
back();
break;
}
}
elseif(!
isdigit(ch)){
back();
break;
}
token[i]=ch;
}
token[i]='\0';
if(token[0]=='.'&&strlen(token)==1){
//error("未预料的符号.\n");
getOperator(ch);
return;
}
if(type==0){
printf("%ld\t%s\t%s\n",line,TYPE[INT],token);
}
elseif(type==1){
printf("%ld\t%s\t%s\n",line,TYPE[FLOAT],token);
}
}
//获得界符与运算符
voidgetDelim(charch){
printf("%ld\t%s\t%c\n",line,TYPE[DELIM],ch);
}
voidlexer(){
pos=0;
line=1;
column=0;
preprocess();
for(;poscharch=readSource();
//printf("%ld%ld%c\n",line,column,ch);
if(isalpha(ch)||ch=='_'){
getIdentify(ch);
}
elseif(isdigit(ch)||ch=='.'){
getNum(ch);
}
elseif(isspace(ch)){
}
elseif(ch=='\''){
getChar(ch);
}
elseif(ch=='"'){
getString(ch);
}
elseif(ch=='/'){
comment();
}
elseif(isoperator(ch)){
getOperator(ch);
}
elseif(isdelim(ch)){
getDelim(ch);
}
else{
printf("%ld\tPRE\t%c\n",line,ch);
}
}
}
/*命令行参数
*lexerfilename
*/
intmain(intargc,char**argv){
if(argc!
=2){
error("Usage:
%sfilename\n",argv[0]);
}
strcpy(filename,argv[1]);
FILE*file;
if(NULL==(file=fopen(argv[1],"r"))){
error("Cannotopen%s.\n",filename);
}
longfileLength=getFileLength(file);
source=(char*)malloc(fileLength+1);
fseek(file,0,SEEK_SET);
inthasRead;
for(hasRead=0;!
feof(file);){
intcount=fread(source+hasRead,1,1024,file);
hasRead+=count;
}
source[hasRead]=0;
length=hasRead;
lexer();//调用词法处理器
fclose(file);
return0;
}
测试程序:
#include
intgetSum(intnum){
inti=0;
intsum=0;
while(i<=num){
sum+=i;
i++;
}
returnsum;
}
/*Thisfunctionistogettwonumandcalculatetheresum
*andprintthesumof12...to10
*/
intmain(){
intt=10;
floata=10E-5,b=5.2;
scanf("%f%f",&a,&b);
printf("a=%f,b=%f\n",a,b);
if(a==b)
printf("aisequaltob\n");
elseif(a
printf("a<=b\n");
else
printf("a>b\n");
//invokeafunction
printf("sum=%d\n",sum(t));
return0;
}
测试结果:
2#include
3KEYint
3IDgetSum
3DELIM(
3KEYint
3IDnum
3DELIM)
3DELIM{
4KEYint
4IDi
4OPT=
4INT0
4DELIM;
5KEYint
5IDsum
5OPT=
5INT0
5DELIM;
6KEYwhile
6DELIM(
6IDi
6OPT<=
6IDnum
6DELIM)
6DELIM{
7IDsum
7OPT+=
7IDi
7DELIM;
8IDi
8OPT++
8DELIM;
9DELIM}
10KEYreturn
10IDsum
10DELIM;
11DELIM}
17KEYint
17IDmain
17DELIM(
17DELIM)
17DELIM{
18KEYint
18IDt
18OPT=
18INT10
18DELIM;
19KEYfloat
19IDa
19OPT=
19FLOAT10E-5
19DELIM,
19IDb
19OPT=
19FLOAT5.2
19DELIM;
21IDscanf
21DELIM(
21STR"%f%f"
21DELIM,
21OPT&
21IDa
21DELIM,
21OPT&
21IDb
21DELIM)
21DELIM;
22IDprintf
22DELIM(
22STR"a=%f,b=%f\n"
22DELIM,
22IDa
22DELIM,
22IDb
22DELIM)
22DELIM;
23KEYif
23DELIM(
23IDa
23OPT==
23IDb
23DELIM)
24IDprintf
24DELIM(
24STR"aisequaltob\n"
24DELIM)
24DELIM;
25KEYelse
25KEYif
25DELIM(
25IDa
25OPT<
25IDb
25DELIM)
26IDprintf
26DELIM(
26STR"a<=b\n"
26DELIM)
26DELIM;
28KEYelse
28IDprintf
28DELIM(
28STR"a>b\n"
28DELIM)
28DELIM;
31IDprintf
31DELIM(
31STR"sum=%d\n"
31DELIM,
31IDsum
31DELIM(
31IDt
31DELIM)
31DELIM)
31DELIM;
32KEYreturn
32INT0
32DELIM;
33DELIM}
实验感想:
1.开始时的程序整体无框架,所有程序都写在了一个函数里,写到中间发现不宜维护,可读性差,后来就整体重新写了一遍,把不相关的功能剥离,提高模块性及可读性。
。
2.开始时注释处理在符号处理后面,后来发现处理不了注释,经过排查后发现把注释处理放到最前面就行了。
3.本程序添加了许多错误处理,使代码更加健壮。