词法分析实验报告.docx
- 文档编号:30216654
- 上传时间:2023-08-07
- 格式:DOCX
- 页数:30
- 大小:240.94KB
词法分析实验报告.docx
《词法分析实验报告.docx》由会员分享,可在线阅读,更多相关《词法分析实验报告.docx(30页珍藏版)》请在冰豆网上搜索。
词法分析实验报告
编译原理实验一
姓名:
朱彦荣
学号:
20132184
专业:
软件工程2
实验题目:
词法分析
完成语言:
C/C++
上级系统:
VC++6.0
日期:
2015/11/7
词法分析
设计题目:
手工设计c语言的词法分析器
(可以是c语言的子集)
设计容:
处理c语言源程序,过滤掉无用符号,判断源程序中单词的合法性,并分解出正确的单词,以二元组形式存放在文件中。
设计目的:
了解高级语言单词的分类,了解状态图以及如何表示并识别单词规则,掌握状态图到识别程序的编程。
结果要求:
课程设计报告。
完成日期:
第十五周提交报告
一.分析
要想手工设计词法分析器,实现C语言子集的识别,就要明白什么是词法分析器,它的功能是什么。
词法分析是编译程序进行编译时第一个要进行的任务,主要是对源程序进行编译预处理(去除注释、无用的回车换行找到包含的文件等)之后,对整个源程序进行分解,分解成一个个单词,这些单词有且只有五类,分别是标识符、保留字、常数、运算符、界符。
以便为下面的语法分析和语义分析做准备。
可以说词法分析面向的对象是单个的字符,目的是把它们组成有效的单词(字符串);而语法的分析则是利用词法分析的结果作为输入来分析是否符合语法规则并且进行语法制导下的语义分析,最后产生四元组(中间代码),进行优化(可有可无)之后最终生成目标代码。
可见词法分析是所有后续工作的基础,如果这一步出错,比如明明是‘<=’却被拆分成‘<’和‘=’就会对下文造成不可挽回的影响。
因此,在进行词法分析的时候一定要定义好这五种符号的集合。
下面是我构造的一个C语言子集。
第一类:
标识符letter(letter|digit)*无穷集
第二类:
常数(digit)+无穷集
第三类:
保留字(32)
autobreakcasecharconstcontinue
defaultdodoubleelseenumextern
floatforgotoifintlong
registerreturnshortsignedsizeofstatic
structswitchtypedefunionunsignedvoid
volatilewhile
第四类:
界符‘/*’、‘//’、(){}[]""'等
第五类:
运算符<、<=、>、>=、=、+、-、*、/、^、等
对所有可数符号进行编码:
<$,0>
...
<+,33>
<-,34>
<*,35>
,36>
<<,37>
<<=,38>
<>,39>
<>=,40>
<=,41>
<==,42>
=,43>
<;,44>
<(,45>
<),46>
<^,47>
<,,48>
<",49>
<',50>
<#,51>
<&,52>
<&&,53>
<|,54>
<||,55>
<%,56>
<~,57>
<<<,58>左移
<>>,59>右移
<[,60>
<],61>
<{,62>
<},63>
<\,64>
<.,65>
66>
<:
67>
68>
"[","]","{","}"
<常数99,数值>
<标识符100,标识符指针>
上述二元组中左边是单词的符号,右边为其种别码,其中常数和标识符有点特别,因为是无穷集合,因此常数用自身来表示,种别码为99,标识符用标识符符号表的指针表示(当然也可用自身显示,比较容易观察),种别码100。
根据上述约定,一旦见到了种别码syn=63,就唯一确定了‘}’这个单词。
下面是一些变量的约定:
//全局变量,保留字表
staticcharreserveWord[32][20]={
"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"
};
//界符运算符表,根据需要可以自行增加
staticcharoperatorOrDelimiter[36][10]={
"+","-","*","/","<","<=",">",">=","=","==",
"!
=",";","(",")","^",",","\"","\'","#","&",
"&&","|","||","%","~","<<",">>","[","]","{",
"}","\\",".","\?
",":
","!
"
};
staticcharIDentifierTbl[1000][50]={""};//标识符表
charresourceProject[10000];//输入的源程序存放处,最大可以存放10000个字符。
chartoken[20]={0};//每次扫描的时候存储已经扫描的结果。
intsyn=-1;//syn即为种别码,约定‘$’的种别码为0,为整个源程序的结束符号一旦扫描到这个字符代表扫描结束
intpProject=0;//源程序指针,始终指向当前源程序待扫描位置。
几个重要函数:
//查找保留字,若成功查找,则返回种别码
//否则返回-1,代表查找不成功,即为标识符
intsearchReserve(charreserveWord[][20],chars[])
/*********************判断是否为字母********************/
boolIsLetter(charletter)
/*****************判断是否为数字************************/
boolIsDigit(chardigit)
/********************编译预处理,取出无用的字符和注释**********************/
voidfilterResource(charr[],intpProject)
/****************************分析子程序,算法核心***********************/
voidScanner(int&syn,charresourceProject[],chartoken[],int&pProject)
下面说一下整个程序的流程:
1.词法分析程序打开源文件,读取文件容,直至遇上’$’文件结束符,然后读取结束。
2.对读取的文件进行预处理,从头到尾进行扫描,去除//和/**/的容,以及一些无用的、影响程序执行的符号如换行符、回车符、制表符等。
但是千万注意不要在这个时候去除空格,因为空格在词法分析中有用,比如说inti=3;这个语句,如果去除空格就变成了“inti=3”,这样就失去了程序的本意,因此不能在这个时候去除空格。
3.选下面就要对源文件从头到尾进行扫描了,从头开始扫描,这个时候扫描程序首先要询问当前的字符是不是空格,若是空格,则继续扫描下一个字符,直至不是空格,然后询问这个字符是不是字母,若是则进行标识符和保留字的识别;若这个字符为数字,则进行数字的判断。
否则,依次对这个字符可能的情况进行判断,若是将所有可能都走了一遍还是没有知道它是谁,则认定为错误符号,输出该错误符号,然后结束。
每次成功识别了一个单词后,单词都会存在token[]中。
然后确定这个单词的种别码,最后进行下一个单词的识别。
这就是扫描程序进行的工作,可以说这个程序彻底实现了确定有限自动机的某些功能,比如说识别标识符,识别数字等。
为了简单起见,这里的数字只是整数。
4.主控程序主要负责对每次识别的种别码syn进行判断,对于不同的单词种别做出不同的反应,如对于标识符则将其插入标识符表中。
对于保留字则输出该保留字的种别码和助记符,等等吧。
直至遇到syn=0;程序结束。
二.流程图
下面是程序的流程图:
三.运行与测试
比如说,就拿这个源程序的一部分进行测试:
运行程序后结果为:
同样单词也写入了文件如下:
。
。
。
综上分析,达到了预期的结果。
四.实验体会
每做一次比较大的实验,都应该写一下实验体会,来加深自己对知识的认识。
其实这次的实验,算法部分并不难,只要知道了DFA,这个模块很好写,比较麻烦的就是五种类型的字符个数越多程序就越长。
但为了能识别大部分程序,我还是用了比较大的子集,结果花了一下午的功夫才写完,虽然很累吧,但看着这个词法分析器的处理能力,觉得还是值得的。
同时也加深了对字符的认识。
程序的可读性还算不错。
程序没有实现的是对所有复合运算的分离,但原理是相同的,比如“+=“,只需在”+“的逻辑之后向前扫描就行了,因此就没有再加上了。
感受最深的是学习编译原理必须要做实验,写程序,这样才会提高自己的动手能力,加深自己对难点的理解,对于以后的求first{},follow{},fisrtVT{},lastVT{}更是应该如此。
五.源程序
//Lexical_Analysis.cpp:
定义控制台应用程序的入口点。
//
#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#include"iostream"
usingnamespacestd;
//词法分析程序
//首先定义种别码
/*
第一类:
标识符letter(letter|digit)*无穷集
第二类:
常数(digit)+无穷集
第三类:
保留字(32)
autobreakcasecharconstcontinue
defaultdodoubleelseenumextern
floatforgotoifintlong
registerreturnshortsignedsizeofstatic
structswitchtypedefunionunsignedvoid
volatilewhile
第四类:
界符‘/*’、‘//’、(){}[]""'
第五类:
运算符<、<=、>、>=、=、+、-、*、/、^、
对所有可数符号进行编码:
<$,0>
...
<+,33>
<-,34>
<*,35>
,36>
<<,37>
<<=,38>
<>,39>
<>=,40>
<=,41>
<==,42>
=,43>
<;,44>
<(,45>
<),46>
<^,47>
<,,48>
<",49>
<',50>
<#,51>
<&,52>
<&&,53>
<|,54>
<||,55>
<%,56>
<~,57>
<<<,58>左移
<>>,59>右移
<[,60>
<],61>
<{,62>
<},63>
<\,64>
<.,65>
66>
<:
67>
68>
"[","]","{","}"
<常数99,数值>
<标识符100,标识符指针>
*/
/****************************************************************************************/
//全局变量,保留字表
staticcharreserveWord[32][20]={
"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"
};
//界符运算符表,根据需要可以自行增加
staticcharoperatorOrDelimiter[36][10]={
"+","-","*","/","<","<=",">",">=","=","==",
"!
=",";","(",")","^",",","\"","\'","#","&",
"&&","|","||","%","~","<<",">>","[","]","{",
"}","\\",".","\?
",":
","!
"
};
staticcharIDentifierTbl[1000][50]={""};//标识符表
/****************************************************************************************/
/********查找保留字*****************/
intsearchReserve(charreserveWord[][20],chars[])
{
for(inti=0;i<32;i++)
{
if(strcmp(reserveWord[i],s)==0)
{//若成功查找,则返回种别码
returni+1;//返回种别码
}
}
return-1;//否则返回-1,代表查找不成功,即为标识符
}
/********查找保留字*****************/
/*********************判断是否为字母********************/
boolIsLetter(charletter)
{//注意C语言允许下划线也为标识符的一部分可以放在首部或其他地方
if(letter>='a'&&letter<='z'||letter>='A'&&letter<='Z'||letter=='_')
{
returntrue;
}
else
{
returnfalse;
}
}
/*********************判断是否为字母********************/
/*****************判断是否为数字************************/
boolIsDigit(chardigit)
{
if(digit>='0'&&digit<='9')
{
returntrue;
}
else
{
returnfalse;
}
}
/*****************判断是否为数字************************/
/********************编译预处理,取出无用的字符和注释**********************/
voidfilterResource(charr[],intpProject)
{
chartempString[10000];
intcount=0;
for(inti=0;i<=pProject;i++)
{
if(r[i]=='/'&&r[i+1]=='/')
{//若为单行注释“//”,则去除注释后面的东西,直至遇到回车换行
while(r[i]!
='\n')
{
i++;//向后扫描
}
}
if(r[i]=='/'&&r[i+1]=='*')
{//若为多行注释“/*。
。
。
*/”则去除该容
i+=2;
while(r[i]!
='*'||r[i+1]!
='/')
{
i++;//继续扫描
if(r[i]=='$')
{
printf("注释出错,没有找到*/,程序结束!
!
!
\n");
exit(0);
}
}
i+=2;//跨过“*/”
}
if(r[i]!
='\n'&&r[i]!
='\t'&&r[i]!
='\v'&&r[i]!
='\r')
{//若出现无用字符,则过滤;否则加载
tempString[count++]=r[i];
}
}
tempString[count]='\0';
strcpy(r,tempString);//产生净化之后的源程序
}
/********************编译预处理,取出无用的字符和注释**********************/
/****************************分析子程序,算法核心***********************/
voidScanner(int&syn,charresourceProject[],chartoken[],int&pProject)
{//根据DFA的状态转换图设计
inti,count=0;//count用来做token[]的指示器,收集有用字符
charch;//作为判断使用
ch=resourceProject[pProject];
while(ch=='')
{//过滤空格,防止程序因识别不了空格而结束
pProject++;
ch=resourceProject[pProject];
}
for(i=0;i<20;i++)
{//每次收集前先清零
token[i]='\0';
}
if(IsLetter(resourceProject[pProject]))
{//开头为字母
token[count++]=resourceProject[pProject];//收集
pProject++;//下移
while(IsLetter(resourceProject[pProject])||IsDigit(resourceProject[pProject]))
{//后跟字母或数字
token[count++]=resourceProject[pProject];//收集
pProject++;//下移
}//多读了一个字符既是下次将要开始的指针位置
token[count]='\0';
syn=searchReserve(reserveWord,token);//查表找到种别码
if(syn==-1)
{//若不是保留字则是标识符
syn=100;//标识符种别码
}
return;
}
elseif(IsDigit(resourceProject[pProject]))
{//首字符为数字
while(IsDigit(resourceProject[pProject]))
{//后跟数字
token[count++]=resourceProject[pProject];//收集
pProject++;
}//多读了一个字符既是下次将要开始的指针位置
token[count]='\0';
syn=99;//常数种别码
}
elseif(ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch==';'||ch=='('||ch==')'||ch=='^'
||ch==','||ch=='\"'||ch=='\''||ch=='~'||ch=='#'||ch=='%'||ch=='['
||ch==']'||ch=='{'||ch=='}'||ch=='\\'||ch=='.'||ch=='\?
'||ch==':
')
{//若为运算符或者界符,查表得到结果
token[0]=resourceProject[pProject];
token[1]='\0';//形成单字符串
for(i=0;i<36;i++)
{//查运算符界符表
if(strcmp(token,operatorOrDelimiter[i])==0)
{
syn=33+i;//获得种别码,使用了一点技巧,使之呈线性映射
break;//查到即推出
}
}
pProject++;//指针下移,为下一扫描做准备
return;
}
elseif(resourceProject[pProject]=='<')
{//<,<=,<<
pProject++;//后移,超前搜索
if(resourceProject[pProject]=='=')
{
syn=38;
}
elseif(resourceProject[pProject]=='<')
{//左移
pProject--;
syn=58;
}
else
{
pProject--;
syn=37;
}
pProject++;//指针下移
return;
}
elseif(resourceProject[pProject]=='>')
{//>,>=,>>
pProject++;
if(resourceProject[pProject]=='=')
{
syn=40;
}
elseif(resourceProject[pProject]=='>')
{
syn=59;
}
else
{
pProject--;
syn=39;
}
pProject++;
return;
}
elseif(resourceProject[pProject]=='=')
{//=.==
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 词法 分析 实验 报告