设计、编制、调试一个词法分析程序,对单词进行识别和编码,加深对词法分析原理的理解。
二、设计内容设计并实现一个词法分析器,实现对指定位置的类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; }
版权声明:本文为博主原创文章,未经博主允许不得转载。