设计、编制、调试一个词法分析程序,对单词进行识别和编码,加深对词法分析原理的理解。
二、设计内容设计并实现一个词法分析器,实现对指定位置的类C 语言源程序文本文件的读取,并能够对该源程
序中的所有单词进行分类,指出其所属类型,实现简单的词法分析操作。
三、实验要求
1、允许用户自己输入程序并保存为文件
2、系统能够输出经过预处理后的源程序(去掉注释、换行、空格等)
3、能够将该源程序中所有的单词根据其所属类型(整数、保留字、运算符、标识符等。定义的类
C 语言中的标识符只能以字母或下划线开头)进行归类显示,例如:识别保留字:if、int、for、while、
do、return、break、continue 等,其他的都识别为标识符;常数为无符号整形数;运算符包括:+、-、
*、/、=、>、<、>=、<=、!=等;分隔符包括:,、;、{、}、(、)等。
4、实现文件的读取操作,而不是将文本以字符串形式预存于程序中。文本内容为待分析的类C 语
言程序。
解决方案:
关于实验要求1:主要涉及的是文件的写入和保存,写入和保存的内容是用户输入的程序。怎么处理的?我的方法是在这部分写两个函数,一个函数用来输入程序:,另一个函数用来保存文件。
关于实验要求2:一个函数,用来清除文本信息中的空格 换行 Tab,同时调用该函数后,将经过预处理的内容保存下来,我是在本地又新建了一个文本文件。
关于实验要求3:这部分是整个实验的关键部分,涉及到对预处理文本的处理。关于这部分的算法思路,在编译原理书上有提到,但是使用Pascal语言写的,但是思想是基本不变的,理解下就比较好些了。我想提的一点是,关于RETRACT函数,即实现“将字符数组指针向前移动一个位置”的功能,请查找fseek()函数的使用。
关于实验要求4:文件的读取函数,readFile()。
自己写的代码实验要求基本都实现了,测了几组也没bug,不过感觉还需要改进,过些日子再贴上来,暂时留这。
--------------------------------------------------------------------2015.10.23更新--------------------------------------------------------------------
之前写的代码不具备处理注释的功能,后来加进去了,需要对上述解决方案做下改动,主要是函数和结构,改动如下:
函数说明:
对读文件函数和写文件函数、预处理文件函数进行了改动,增加注释预处理部分dealNote()。
程序运行过程:先由用户在键盘上写程序,保存文件至G:\contextfile.txt,对注释部分进行处理,保存处理后的文件至G:\nonotecontextfile.txt,再对该文件进行在空格、Tab键和空格上的预处理,最后进行judge判断。
测试数据如下:
/*
{
int a,b;
a = 10;
b = a + 20; /*dsdsdsdsd*/
}
*/
代码:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <fstream>
#include <string.h>
#include <math.h>
using namespace std;
const int LEN = 0Xfff;
char fsm[8][128]; //清除注释,做一个状态机
FILE *fp; //文件指针
char CHAR; //字符变量CHAR,它存放着新读入的源程序字符
string TOKEN; //字符串TOKEN,它存放着构成单词符号的字符串
string afterPreDeal; //存放着经过预处理后的程序内容
string TABLE[LEN] = {"if", "int", "for", "while", "do", "return", "break", "continue", "else"}; //保留字
void inputFile(); //输入程序内容(允许换行、空格和Tab键)
void dealNote();
void saveToFile(char *p, char * filename); //将输入的程序内容保存到文件里
void judge(char * filename); //对单词进行归类显示
string& trim(string &str, string::size_type pos = 0); //去除文本文件中的空格、Tab和换行
void readFile(char * filename); //读取文件内容并输出
void preDeal(char * filename); //对程序文件做预处理,并将预处理后的内容保存在新文件G:\newcontextfile.txt中
void GETCHAR(); //用于读取下一个原程序字符至CHAR中,并把字符指针向后移一位
void GETNBC(); //先检查CHAR是否为空白字符,若是则反复调用GETCHAR直到CHAR读入的是一个非空白字符
void CONCAT(); //将CHAR字符连接到TOKEN后面
bool LETTER(char CHAR); //布尔函数,若CHAR为字母则返回TRUE,反之返回FALSE;
bool DIGIT(char CHAR); //布尔函数,若CHAR为数字则返回TRUE,反之返回FALSE;
bool UNDERLINE(char CHAR); //布尔函数,若CHAR为下划线则返回TRUE,反之返回FALSE;
int RESERVE(); //由TOKEN字符串查保留字表,若TOKEN中字符串为保留字则返回其种别编码,否则返回值为0
void RETRACT(); //将字符指针向前移动一个位置,CHAR置为空白字符
void initfsm();
int main()
{
//保存内容
cout << "*********你好,请输入程序内容(以Ctrl+Z结束)*********" << endl;
inputFile();
dealNote(); //清除注释
preDeal("G:\nonotecontextfile.txt"); //对清除注释后的文件进行其他预处理,比如去空格,去tab,去换行
cout << "*********输出判断结果*********" << endl;
judge("G:\newcontextfile.txt");
cout << endl;
return 0;
}
void inputFile()
{
char s[10] = {' '};
fstream file("G:\contextfile.txt", ios::out);
while((scanf("%c",&s))!=EOF)
{
file.write(s, strlen(s));
}
file.close();
}
void dealNote()
{
int state=0;
char c;
std::string s;
FILE *fin=fopen("G:\contextfile.txt","r");
FILE *fout=fopen("G:\nonotecontextfile.txt","w");
initfsm();
while(fscanf(fin,"%c",&c)!=EOF)
{
state=fsm[state][c];
s+=c;
switch(state)
{
case 0:
fprintf(fout,"%s",s.c_str());
s="";
break;
case 7:
s="";
if(c=='
')
{
fputc(c,fout);
}
break;
}
}
fclose(fin);
fclose(fout);
}
void saveToFile(char *p, char * filename)
{
if ((fp = fopen(filename, "wb")) == NULL )
{
return;
}
else
{
fwrite(p, strlen(p), 1, fp);
}
fclose(fp);
}
void judge(char *filename)
{
fp = fopen(filename, "r+");
while(!feof(fp))
{
TOKEN.clear();
GETCHAR();
if(LETTER(CHAR))
{
int c;
int flag = 0;
while(LETTER(CHAR) || DIGIT(CHAR) || UNDERLINE(CHAR))
{
CONCAT();
GETCHAR();
{
c = RESERVE();
if(c == 2)
{
cout << "(1," << """ << TOKEN << """ << ")" << endl;
flag = 1;
break;
}
}
}
RETRACT();
if(flag == 0)
{
c = RESERVE();
if(c == 0)
cout << "(2," << """ << TOKEN << """ << ")" << endl;
else
cout << "(1," << """ << TOKEN << """ << ")" << endl;
}
continue;
}
else if(UNDERLINE(CHAR))
{
int c;
int flag = 0;
while(LETTER(CHAR) || DIGIT(CHAR) || UNDERLINE(CHAR))
{
CONCAT();
GETCHAR();
{
c = RESERVE();
if(c == 2)
{
cout << "(1," << """ << TOKEN << """ << ")" << endl;
flag = 1;
break;
}
}
}
RETRACT();
if(flag == 0)
{
c = RESERVE();
if(c == 0)
cout << "(2," << """ << TOKEN << """ << ")" << endl;
else
cout << "(1," << """ << TOKEN << """ << ")" << endl;
}
continue;
}
else if(DIGIT(CHAR))
{
while(DIGIT(CHAR))
{
CONCAT();
GETCHAR();
}
RETRACT();
cout << "(3," << """ << TOKEN << """ << ")" << endl;
continue;
}
else if(CHAR == ',' || CHAR == ';' || CHAR == '{' || CHAR == '}' || CHAR == '(' || CHAR == ')')
{
cout << "(5," << """ << CHAR << """ << ")" << endl;
continue;
}
else if( CHAR == '+' || CHAR == '-' || CHAR == '*' || CHAR == '\' || CHAR == '=' )
{
char mmd = CHAR;
GETCHAR();
if(CHAR == '=')
{
cout << "(4," << """ << mmd << """ << "=)" << endl;
}
else
{
RETRACT();
cout << "(4," << """ << mmd << """ << ")" << endl;
}
continue;
}
else if(CHAR == ' '|| CHAR == '
' || CHAR == ' ')
{
continue;
}
else
{
if((int)CHAR == -1)
return;
// cout << "ERROR Message" << endl;
}
}
fclose(fp);/*关闭文件*/
}
string& trim(string &str, string::size_type pos)
{
static const string delim = "
"; //删除空格或者tab字符
pos = str.find_first_of(delim, pos);
if (pos == string::npos)
return str;
return trim(str.erase(pos, 1));
}
void readFile(char * filename)
{
FILE *pFile=fopen(filename, "r"); //获取文件的指针
char *pBuf; //定义文件指针
fseek(pFile, 0, SEEK_END); //把指针移动到文件的结尾 ,获取文件长度
int len = ftell(pFile); //获取文件长度
pBuf = new char[len+1]; //定义数组长度
rewind(pFile); //把指针移动到文件开头 因为我们一开始把指针移动到结尾,如果不移动回来 会出错
fread(pBuf,1,len,pFile); //读文件
pBuf[len]=0; //把读到的文件最后一位 写为0 要不然系统会一直寻找到0后才结束
printf("%s", pBuf); //显示读到的数据
fclose(pFile); // 关闭文件
}
void preDeal(char * filename) //对程序文件做预处理,保存在新文件G:\newcontextfile.txt中
{
// dealNote();
FILE *pFile=fopen(filename, "r"); //获取文件的指针
char *pBuf; //定义文件指针
fseek(pFile, 0, SEEK_END); //把指针移动到文件的结尾 ,获取文件长度
int len = ftell(pFile); //获取文件长度
pBuf = new char[len+1]; //定义数组长度
string predeal;
int n = len;
if ((fp = fopen(filename, "rb")) == NULL )
{
return;
}
else
{
char p[100];
fread(p, n, 1, fp);
p[n]=' ';
predeal = p;
}
predeal = trim(predeal);
char *con = new char[predeal.length() + 10];
strcpy(con, predeal.c_str());
saveToFile(con, "G:\newcontextfile.txt"); //将新文件内容保存为G:\newcontextfile.txt
cout << endl;
afterPreDeal = predeal;
fclose(fp);/*关闭文件*/
}
void GETCHAR()
{
CHAR = fgetc(fp);
// cout << "*" << CHAR << "*" << endl;
}
void GETNBC()
{
while(CHAR == ' ' || CHAR == '
' || CHAR == ' ')
{
GETCHAR();
}
}
void CONCAT()
{
TOKEN += CHAR;
}
bool LETTER(char CHAR)
{
bool isLetter = isalpha(CHAR);
return isLetter;
}
bool DIGIT(char CHAR)
{
bool isDigit = isdigit(CHAR);
return isDigit;
}
bool UNDERLINE(char CHAR)
{
if(CHAR == '_')
return true;
else
return false;
}
int RESERVE()
{
int i = 0;
int N = 9;
int val = 0;
for(i = 0; i < N; i++)
{
if(TOKEN == TABLE[i])
{
val = 2;
break;
}
}
return val;
}
void RETRACT()
{
CHAR = ' ';
fseek(fp,-1,SEEK_CUR);
}
void initfsm()
{
const int line_len = sizeof(char)*128;
memset(fsm[0],0,line_len);
memset(fsm[1],0,line_len);
memset(fsm[2],2,line_len);
memset(fsm[3],3,line_len);
memset(fsm[4],3,line_len);
memset(fsm[5],5,line_len);
memset(fsm[6],5,line_len);
memset(fsm[7],0,line_len);
fsm[0]['/'] = 1;
fsm[0]['"'] = 5;
fsm[1]['/'] = 2;
fsm[1]['*'] = 3;
fsm[1]['"'] = 5;
fsm[2]['
'] = 7;
fsm[3]['*'] = 4;
fsm[4]['/'] = 7;
fsm[4]['*'] = 4;
fsm[5]['"'] = 0;
fsm[5]['\'] = 6;
fsm[7]['/'] = 1;
fsm[7]['"'] = 5;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。