资源描述
《编译原理》课程设计报告
专业: 计算机科学与技术
学生姓名: 张金荣
指导教师: 孔繁茹
完成时间: 2025年8月5日
2011—2012学年第一学期
9
- -
目 录
第 1 节 课程设计目的 1
第 2 节 课程设计要求 1
第 3 节 课程设计内容 2
第 4 节 状态转换图 4
第 5 节 实现工具 4
第 6 节 实现函数 4
第 7 节 测试结果 5
第 8 节 实验源代码 6
第 9 节 参考文献 9
致谢
感谢孔繁茹老师在课程设计中的大力支持及给予的无私帮助
1. 设计目的
①理解词法分析器的基本功能。词法分析的任务是:从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号(token),把作为字符串的源程序改造成 单词符号串的中间程序。因此,词法分析是编译的基础。
②理解词法规则的描述方法。程序设计语言一般可以用标识符、关键字、运算符、分隔符、常量、字符串和注释符来描述
④理解状态转换图及其实现。一个状态转换图可用于识别(或接受)一定的字符。 大多数程序语言的单词符号都可以用转换图予以识别。 转换图非常易于用程序实现,最简单的办法是让每个状态结对应一小段程序。
④能够编写简单的词法分析器。
2.课程设计的要求
手工构造一个简单的词法分析程序, 能够识别标识符、整数、关键字、算符、界符。
①画出识别单词的状态转换图。
(若状态转换图过于复杂,可以只画出主要部分;若依旧复杂,可只识别标识符和整数)
②根据状态转换图手工构造词法分析程序。从以下方法中选一:
² 词法分析器作为独立的一遍。
± 词法分析结果输出到屏幕上或存入文件。
² 词法分析器作为一个子程序被语法分析器调用。
± 每次调用返回一个单词
± 同时将单词及属性存入符号表
③ 实现状态转换图。从以下方法中选一:
² 直接转向法
² 表驱动法
四、选做实验
☻ 使用缓冲技术
3.课程设计内容
程序语言的单词符号一般可分为下列五种。
(1)关键字
是由程序语言定义的具有固定意义的标志符。本程序定义 char,short,int,unsigned,long,float,double,struct,union,void,enum,const,typedef,auto,static,break,case,continue,default,do,else,for,if,return,switch,while,sizeof,printf,FILE,fopen,NULL,fclose,exit,read,closef,printf为关键字。
(2)标识符
用来表示各种名字,如变量名、数组名、过程名等等。
(3)常数
常数的类型一般有整型、实型、布尔型、文字型等等。例如,100,3.14159。
(4)运算符
如+、-、*、/、>=、<=、== 等等
(5)界符
如[、]、{、}、(、)、;、,、.等等。
具体实现过程:
用c++编写词法分析程序,从文件中读入预分析的源程序经词法分析将结果写入指定文件中。
在设计中预分析文件保存在test.txt中,输出结果保存在result.txt中。
本程序实现了
将词法分析器作为独立的一遍,词法分析结果存入文件。
用直接转向法实现状态转换图。
☻使用缓冲技术。
本程序能够识别简单的运算符,界符,常数,标示符和关键字。其中关键字由程序所给的关键字表kt(key table)内容指定。如有未涉及的关键字还可以通过修改程序中表内容扩展。对未定义的字符(不属于界符,运算符和常数)输出 并显示为“其他字符”。文中特别定义了注释符号,换行符号和空格。在程序设计工程中一开始未注意将当前指针后退一位的情况,在输出程序结果时发现一些字符缺漏。通过查资料【1】发现了将指针后移的用法并解决了这个问题。设计过程中牵涉到对文件的使用如c_str(),ios:binary的用法,get()函数的用法,fstream的两个类ifstream和ofstream的用法【2】实现文件的读写操作。
为了分析大于4kb的程序本程序使用了著名的双缓冲技术。设计思想如下
4.状态转换图如下
5.实验工具
Vc++6.0 ,win7画图工具,
6. 实现函数
void get_char (); //用于从buffer读取一个字符到C
int letter (int C); //判断是否是字母
int digit (int C); //判断时候是数字或者小数点
int underline (int C); //判断时候是下划线
int reserve (string token); //判断时候是关键字
void retract (); //向前指针向后退一个
void returnout (string str1,string str2); //文件打印
7. 结果及测试分析
实验代码
/////////////////////////////////////////// 词法分析程序@zhangjinrong//////////////////////////////////////////////////
/////////头文件////////////
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//关键字///////////
string kt[36] ={"char","short","int","unsigned","long","float","double","struct","union","void",
"enum","const","typedef","auto","static","break","case","continue","default","do","else","for","if",
"return","switch","while","sizeof","printf","FILE","fopen","NULL",
"fclose","exit","read","close","fprintf"};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//自定义变量,数组//
ifstream infile; //以输入(从硬盘到内存)方式打开文件
ofstream outfile; //输出(从内存到硬盘)方式打开文件
char buffer1 [64]; //两个缓冲buffer
char buffer2 [64];
char C; //用于switch分析
string token=""; //多个C组成的串
char *startptr,*forwardptr; //buffer的开始指针和向前指针
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//函数声明//////////
void get_char (); //用于从buffer读取一个字符到C
int letter (int C); //判断是否是字母
int digit (int C); //判断时候是数字或者小数点
int underline (int C); //判断时候是下划线
int reserve (string token); //判断时候是关键字
void retract (); //向前指针向后退一个
void returnout (string str1,string str2); //文件打印
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
main ()
{
startptr=buffer1; //数组头指针
forwardptr=buffer1; //数组当前指针
buffer1 [63]=buffer2 [63]=-1; //数组尾数值为-1
cout<<"输入源文件的路径和文件名:(例: F:1.c)"<<endl;
string str111;
cin>>str111;
infile.open (str111.c_str (),ios::binary);//二进制文件
cout<<"输入经词法分析后生成文件的路径和文件名:"<<endl;
string str222;
cin>>str222;
outfile.open (str222.c_str (),ios::trunc);//再次写入覆盖文件已有内容
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///将文件内容读入第一缓冲区/////
for (int i=0;i<=62;i++)
buffer1 [i]=infile.get ();//将文件中字符放入第一缓冲区
while (1)
{
token=""; //多个C组成的串为空
get_char (); //从数组中传递出一个字符c
switch (C)
{
////当首位字符为字母////////////////////////////////////////////////////////////////////////////////////////////////////
case 'a':case 'b':case 'c':case 'd':case 'e': case 'f': case 'g': case 'h': case 'i': case 'j':
case 'k':case 'l':case 'm':case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case 't':
case 'u':case 'v':case 'w':case 'x':case 'y': case 'z': case 'A': case 'B': case 'C': case 'D':
case 'E':case 'F':case 'G':case 'H':case 'I':case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':
case 'P':case 'Q':case 'R':case 'S':case 'T':case 'U':case 'V':case 'W':case 'X':case 'Y':case 'Z':
{
while (letter (C)||digit (C)||underline (C)) //字母后跟字母或数字或下划线时
{
token=token+C;
get_char ();
}
if (reserve (token)==1) //如果字符串不在关键字表中输出标示符
{
returnout ("关键字",token);
}else
returnout (token,"标示符"); //如果在关键字表中输出标示符
retract (); /*当token已为关键字时由于执行了get_char()已读取一个字符。
为避免漏掉需回溯一个字符*/
break;
}
///// 当首字母为数字时 ////////////////////////////////////////////////////////////////////////////////////////////////
case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':case '0':
{
while (digit (C)==1||C=='.')
{
token=token+C;
get_char ();
}
returnout ("常数",token);
retract ();
break;
}
//////本程序定义为运算符所以考虑不同情况////////////////////////////////////////////////////////////////////////
case '<':
{
get_char (); //字符为<,<=,<>三种情况
if (C=='>')
returnout ("<>","空格分界符");
else
{
retract ();
returnout ("<","运算符");
}
break;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
case '/':
{
get_char ();
if (C=='*')// 读到/*的情况一直读到*/结束
{
while (1)
{
get_char ();
while (C!='*')
get_char ();
if (C=='/')
break;
retract ();
}
}
else if (C=='/') //读到//的情况一直读取字符直到这一行结束
{
get_char ();
while (C!='\n')
get_char ();
}
else
{
retract ();
returnout ("/","运算符");
}
break;
}
////////////简单的运算符和界符//////////////////////////////////////////////////////////////////////////////
case'+' :{returnout ("+","运算符");break;}
case'-' :{returnout ("-","运算符");break;}
case'*' :{returnout ("*","运算符");break;}
case'=' :{returnout ("=","运算符");break;}
case'>' :{returnout (">","运算符");break;}
//case'/' :{returnout ("/","运算符");break;}
case'(' :{returnout ("(","界符"); break;}
case')' :{returnout (")","界符"); break;}
case'[' :{returnout ("[","界符"); break;}
case']' :{returnout ("]","界符"); break;}
case';' :{returnout (";","界符"); break;}
case'.' :{returnout (".","界符"); break;}
case',' :{returnout (",","界符"); break;}
case'{' :{returnout ("{","界符"); break;}
case'}' :{returnout ("}","界符"); break;}
/////////////换行和空格/////////////////////////////////////////////////////////////////////////////////////////
case ' ': break;
case '\n': case'\r': //遇到换行符
{
if (C=='\n')
break;
}
default:
{
outfile<<"< "<<C<<" , 换行符>"<<endl;
break;
}
}
}
infile.close ();
outfile.close ();
system ("pause");
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////双缓冲的实现////
void get_char ()
{
if ((* forwardptr)==-1) //当前指针在缓冲区(不知是那个缓冲区)末尾
{
if (forwardptr==buffer1+63) //在第一buffer尾,向第二buffer读入数据
{
for (int i=0;i<=62;i++)
buffer2 [i]=infile.get ();
forwardptr=buffer2;
get_char ();
}
else if (forwardptr==buffer2+63) //在第二buffer尾,向第一buffer读入数据
{
for (int i=0;i<=62;i++)
buffer1 [i]=infile.get ();
forwardptr=buffer1;
get_char ();
}
else //在不是buffer尾的位置读到了EOF,说明完成了分析
{
infile.close ();
outfile.close ();
ofstream out;
cout<<"词法分析结果已存入文件夹中"<<endl;
system ("pause");
exit (-1);
}
}
{
C=(*forwardptr); //当前字符用于分析
forwardptr=forwardptr+1; //当前指针改指向下一个字符
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
///自定义简单函数////
int letter (int C)
{
if ((C>=65&&C<=90)||(C>=97)&&(C<=122))
return 1;
else return 0;
}
int digit (int C)
{
if ((C>=48)&&(C<=57))
return 1;
else return 0;
}
int underline (int C)
{
if (C=='_')
return 1;
else return 0;
}
int reserve (string token)
{
for (int i=0;i<=35;i++)
{
if (token==kt [i])
return 1;
}
return 0;
}
void retract ()
{
if (forwardptr==buffer1) //向前指针在buffer头
forwardptr=(buffer2+63); //后退一个字符在第二缓冲区末尾
else
{
if (forwardptr==buffer2) //使向前指针在第二个buffer尾
forwardptr=(buffer1+63);
else forwardptr--;
}
}
void returnout (string str1,string str2)
{
outfile<<"< "<<str1<<" , "<<str2<<" >"<<endl;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////YANTAN//UNIVERSITY///SC/09/1/1/2009/2550/1128/qq/1013415421//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
参考文献
【1】百度文库《fseek用法详解》
【2】百度文库《c和c++文件读写操作》
【3】程序设计语言编译原理》国防 陈火旺
【4】词法分析器的设计
展开阅读全文