目录
(注:这部分可以点击右键更新生成,重新设置字体为四号)
当今时代是飞速发展的信息时代,随着计算机技术的普及与网络的广泛应用,计算机语言更显得至关重要。计算机语言是人与计算机之间传递信息的媒介。计算机系统最大特征是指令通过一种语言传达给机器。为了使电子计算机进行各种工作,就需要有一套用以编写计算机程序的数字、字符和语法规划,由这些字符和语法规则组成计算机各种指令(或各种语句)。这些就是计算机能接受的语言。一个高级语言程序的实现,必须依赖于相应的编译系统。编译程序的基本任务是将源语言程序翻译成等价的目标语言程序。
词法分析阶段是编译过程的第一个阶段,是编译的基础。词法分析结果的好坏将直接影响中文信息处理上层应用的效果。理解词法分析在编译程序中的作用,加深对有穷自动机模型的理解,掌握词法分析程序的实现方法和技术,加深对编译原理的理解,掌握编译程序的实现方法和技术至关重要。
本次项目则为简单的词法分析器,通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。具体方法为:编制一个读单词的程序,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符和分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示)。
词法分析器相关知识:
1.词法分析器的功能和输出格式
词法分析器的功能是输入源程序,输出单词符号。词法分析器的单词符号常常表示成以下的二元式(单词种别码,单词符号的属性值)。在本实验中,采用的是一类符号一种别码的方式。
标识符的BNF表示:
<标识符>-> <字母><字母数字串>
<字母数字串>-><字母><字母数字串>|<数字><字母数字串>|ε
无符号整数的BNF表示:
<无符号整数>-> <数字><数字串>
<数字串>-> <数字><数字串> |ε
运算符的BNF表示:
<加法运算符>-> +
<减法运算符>-> -
<大于关系运算符>-> >
<大于等于关系运算符>-> >=
2.超前搜索
词法分析时,常常会用到超前搜索方法。如当前待分析字符串为“a>i”,当前字符为’>’,此时,分析器到底是将其分析为大于关系运算符还是大于等于关系运算符呢?显然,只有知道下一个字符是什么才能下结论。于是分析器读入下一个字符’+’,这时可知应将’>’解释为大于运算符。但此时,超前读了一个字符’i’,所以要回退一个字符,词法分析器才能正常运行。在分析标识符,无符号整数等时也有类似情况。
简单流程如图1.1
图1.1程序简单流程图
二、系统设计(三号,宋体,粗体)
(一)系统中的数据定义(四号,黑体,粗体)
定义:词法分析器的功能输入源程序,按照构词规则分解成一系列单词符号。单词是语言中具有独立意义的最小单位,包括关键字、标识符、运算符、界符和常数等。
- 识别保留字:if、int、for、while、do、return、break、continue等
- 运算符包括:+、-、*、/、=、>、<、>=、<=、!=
- 分隔符包括:,、;、{、}、(、)
- 常数为无符号整形数;
- 其它的都识别为标识符;
输出:词法分析器所输出单词符号常常表示成如下的二元式:(单词,单词符号的属性值)
标识符一般统归为一种。常数包括整数和浮点数两种。
关键字、运算符、界符可将其全体视为一种。
单词符号的属性是指单词符号的特性或特征。
功能流程图如图:
实现词法分析的函数为
/**词法分析**/
void analyse(FILE * fpin){
char ch=' ';
string arr="";
while((ch=fgetc(fpin))!=EOF){
arr="";
if(IsFilter(ch)){} //判断是否为过滤符
else if(IsLowLetter(ch)){ //判断是否为关键字
while(IsLowLetter(ch)){
arr += ch;
ch=fgetc(fpin);
}
//fseek(fpin,-1L,SEEK_CUR);
if(IsKeyword(arr)){
cout<<arr<<" <关键字>"<<endl;
}
else
{
cout<<arr<<" <标识符>"<<endl;
}
}
else if(IsDigit(ch)){ //判断是否为数字
while(IsDigit(ch)||(ch=='.'&&IsDigit(fgetc(fpin)))){
arr += ch;
ch=fgetc(fpin);
}
fseek(fpin,-1L,SEEK_CUR);
cout<<arr<<" <无符号整形数>"<<endl;
}
else if(IsUpLetter(ch)||IsLowLetter(ch)||ch=='_'){
while(IsUpLetter(ch)||IsLowLetter(ch)||ch=='_'||IsDigit(ch)){
arr += ch;
ch=fgetc(fpin);
}
fseek(fpin,-1L,SEEK_CUR);
cout<<arr<<" <标识符>"<<endl;
}
else switch(ch){
case '+':
case '-':
case '*':
case '/':
case '>':
case '<':
case '=':
case '!':
{
arr += ch;
cout<<arr<<" <运算符>"<<endl;
break;
}
case ';':
case ',':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
{
arr += ch;
cout<<arr<<" <分隔符>"<<endl;
break;
}
default :cout<<"\""<<ch<<"\":无法识别的字符!"<<endl;
}
}
}
1.词法分析程序打开源文件,读取文件内容,直至遇上’$’文件结束符,然后读取结束。
2.对读取的文件进行预处理,从头到尾进行扫描,去除//和/* */的内容,以及一些无用的、影响程序执行的符号如换行符、回车符、制表符等。但是千万注意不要在这个时候去除空格,因为空格在词法分析中有用,比如说int i=3;这个语句,如果去除空格就变成了“inti=3”,这样就失去了程序的本意,因此不能在这个时候去除空格。
3.接下来就要对源文件从头到尾进行扫描了,从头开始扫描,主控程序主要负责系统建立一个文件保存四个表,这四个表分别存储关键字、运算符、界符、过滤符。而标识符和常数则用正则表达式判断。建立了多个布尔类,当系统读取代码时,用空格或制表符作为切词标志符,当遇到空格就输出之前检索的字符串进行判断(规定每个单词符号之间都有空格),判断字符串时,系统会通过顺序查找依次调用布尔类与之匹配来判断其属性并输出,如没有匹配成功,则说明所检索的字符进步串不合法,系统则会输出非法字符串。直到最后一个字符串匹配完毕之后系统结束
(四)系统的核心算法
(一)/**判断是否为关键字**/
bool IsKeyword(string word){
for(int i=0;i<15;i++){
if(KEYWORD[i]==word){
return true;
}
}
return false;
}
(二)/**判断是否为分隔符**/
bool IsSeparater(char ch){
for(int i=0;i<8;i++){
if(SEPARATER[i]==ch){
return true;
}
}
return false;
}
(三)/**判断是否为运算符**/
bool IsOperator(char ch){
for(int i=0;i<8;i++){
if(OPERATOR[i]==ch){
return true;
}
}
return false;
}
(四)/**判断是否为过滤符**/
bool IsFilter(char ch){
for(int i=0;i<4;i++){
if(FILTER[i]==ch){
return true;
}
}
return false;
}
(五)/**判断是否为大写字母**/
bool IsUpLetter(char ch){
if(ch>='A' && ch<='Z') return true;
return false;
}
(六)/**判断是否为小写字母**/
bool IsLowLetter(char ch){
if(ch>='a' && ch<='z') return true;
return false;
}
(七)/**判断是否为数字**/
bool IsDigit(char ch){
if(ch>='0' && ch<='9') return true;
return false;
}
三、系统编码及运行(三号,宋体,粗体)
(一)系统开发涉及的软件
代码编写工具:CodeBlocks
画图工具:Microsoft Office Word
文档撰写:Microsoft Office Word,记事本
(二)系统运行界面及结果
(一):系统开始界面(如图1)
图1 系统登录界面
(二)系统开始运行界面
第一步:输入源文件名(包含路径和后缀)(如图2)
图2 系统输入源文件界面
(三)运行结果显示界面(如图3)
图3 系统运行结果显示界面
四、系统测试(三号,宋体,粗体)
系统测试数据1:C:\Users\24125\Desktop\a.txt
文本文档内容:
main()
{
int a,b;
a = 10;
b = a + 20;
}
运行结果如下:
系统测试数据2:C:\Users\24125\Desktop\b.txt
文本文档内容:
FILE* fp = NULL;
int ifind = 0;
unsigned char* ptr = NULL;
int datalong = 0;
int g_iLogonID = -1;
unsigned int g_uConnID = -1;
int main()
{
do
{
Sleep(20 * 1000);
} while (0);
return 0;
}
系统运行结果如下图:
系统测试数据3:如果输入了不正确的文件路径
系统测试数据4:如果文本文档中的内容不在词法分析的范围内
这一部分的系统测试,我首先分析了短文本文档内容的C语言程序代码的词法分析器,据结果显示,系统将会按照规定好的关键字,标识符,分界符,无符号型整数等正确显示出词法分析的结果,然后我设置了C语言程序代码较长的文本文档作为词法分析的内容,据结果显示,可得出结论,不论代码的长短,此词法分析器均可以显示正确词法分析结果,然后我例举了可能由于输入错误源文件路径或者名字的情况,据结果显示,如果发生此类情况,词法分析器会出现提示文字,告知用户,重新输入源文件的路径和名字,体现此词法分析器项目的人文关怀,同样,如果待分析文本文档出现不合规字符,此词法分析器同样会给与提示与警告。
五、总结
该系统为简单的词法分析器,可以对输入的字符串进行分析,判断是关键字、标识符、运算符、界符和常数,然后输出。整个系统项目开发的过程中,提高了我对编译原理这门学科的实践能力,加深了我对其的理解,一学期的学习只是限于书本层次和实验层次,只有亲手自己动手动脑,制作出词法分析器,才从根本上理解词法分析的原理,这也让我深深地体会到学习,就应该是书本知识结合实践过程,实践出真知,才能真正将所学的知识融会贯通,把印在纸页上的文字变成自己脑中的能量。而且一定要深知一个大的程序是由许多小的程序组成的,总的程序就是对这些小的程序的调用,来实现各项功能。只要处理好这些小的程序,做出一个大的程序并不是很困难。而且要认真的把自己所学的知识结合起来,比如编译原理和C++,根据C++课程所学的概念、理论和方法,按照C++程序设计的基本步骤,设计出一个适当规模的程序;进一步加深对C++语言和编译原理的理解和掌握。理论联系实际,加深和巩固所学的理论知识,进而提高实践能力和计算机的综合运用能力。
我们编写程序的过程是辛苦与快乐的,程序的编写原则很重要,只要我们在编程,就必须不断改进,才能更好提高编程能力。在这过程中,我也遇到了各种各样的编译错误,或者代码想不通,不知道如果继续下去,我积极的问了自己的同学老师,让我体会到团队的力量,有时候一个人走入一个思想盲点会不断撞墙而无法走出,而与志同道合者交流,则可以让问题迎刃而解,共同努力和进步的感觉让成功变得不是那么遥远。这个程序也是通过多次调试和改进最终才得以完善序,而我自己也在调试的过程中学习的知识得到了完善和补充,不仅是对词法分析器的理解更进一步。也让我重新熟悉了计算机语言的相关内容,加深了知识的深化和用途的理解。未来可能会接触更加复杂和难以理解的知识,所以我会更加努力学习,一面增加提高自己的基础能力,一面提高自身应对困难的能力,力求不断进步。
附录(源代码,注意代码格式)
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
string KEYWORD[15]={"if","else","void","return","while","then","for","main", "int","char","double","float","case","cin","cout"};
char SEPARATER[8]={';',',','{','}','[',']','(',')'}; //界符
char OPERATOR[8]={'+','-','*','/','>','<','=','!'}; //运算符
char FILTER[4]={' ','\t','\r','\n'}; //过滤符
/**判断是否为关键字**/
bool IsKeyword(string word){
for(int i=0;i<15;i++){
if(KEYWORD[i]==word){
return true;
}
}
return false;
}
/**判断是否为分隔符**/
bool IsSeparater(char ch){
for(int i=0;i<8;i++){
if(SEPARATER[i]==ch){
return true;
}
}
return false;
}
/**判断是否为运算符**/
bool IsOperator(char ch){
for(int i=0;i<8;i++){
if(OPERATOR[i]==ch){
return true;
}
}
return false;
}
/**判断是否为过滤符**/
bool IsFilter(char ch){
for(int i=0;i<4;i++){
if(FILTER[i]==ch){
return true;
}
}
return false;
}
/**判断是否为大写字母**/
bool IsUpLetter(char ch){
if(ch>='A' && ch<='Z') return true;
return false;
}
/**判断是否为小写字母**/
bool IsLowLetter(char ch){
if(ch>='a' && ch<='z') return true;
return false;
}
/**判断是否为数字**/
bool IsDigit(char ch){
if(ch>='0' && ch<='9') return true;
return false;
}
/**词法分析**/
void analyse(FILE * fpin){
char ch=' ';
string arr="";
while((ch=fgetc(fpin))!=EOF){
arr="";
if(IsFilter(ch)){} //判断是否为过滤符
else if(IsLowLetter(ch)){ //判断是否为关键字
while(IsLowLetter(ch)){
arr += ch;
ch=fgetc(fpin);
}
//fseek(fpin,-1L,SEEK_CUR);
if(IsKeyword(arr)){
cout<<arr<<" <关键字>"<<endl;
}
else
{
cout<<arr<<" <标识符>"<<endl;
}
}
else if(IsDigit(ch)){ //判断是否为数字
while(IsDigit(ch)||(ch=='.'&&IsDigit(fgetc(fpin)))){
arr += ch;
ch=fgetc(fpin);
}
fseek(fpin,-1L,SEEK_CUR);
cout<<arr<<" <无符号整形数>"<<endl;
}
else if(IsUpLetter(ch)||IsLowLetter(ch)||ch=='_'){
while(IsUpLetter(ch)||IsLowLetter(ch)||ch=='_'||IsDigit(ch)){
arr += ch;
ch=fgetc(fpin);
}
fseek(fpin,-1L,SEEK_CUR);
cout<<arr<<" <标识符>"<<endl;
}
else switch(ch){
case '+':
case '-':
case '*':
case '/':
case '>':
case '<':
case '=':
case '!':
{
arr += ch;
cout<<arr<<" <运算符>"<<endl;
break;
}
case ';':
case ',':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
{
arr += ch;
cout<<arr<<" <分隔符>"<<endl;
break;
}
default :cout<<"\""<<ch<<"\":无法识别的字符!"<<endl;
}
}
}
int main()
{
char inFile[40];
FILE *fpin;
cout<<"********************************************************"<<endl;
cout<<"***********************词法分析器***********************"<<endl;
cout<<"**********************Code Analyse**********************"<<endl;
cout<<"*********************小仙女:赵琳娜**********************"<<endl;
cout<<"***********************软件16-1班***********************"<<endl;
cout<<"********************************************************"<<endl;
cout<<" "<<endl;
cout<<" "<<endl;
cout<<"请输入源文件名(包括路径和后缀):";
while(true){
cin>>inFile;
if((fpin=fopen(inFile,"r"))!=NULL)
break;
else{
cout<<"文件名错误!"<<endl;
cout<<"请输入源文件名(包括路径和后缀):";
}
}
cout<<"------词法分析如下------"<<endl;
analyse(fpin);
return 0;
}
()