設計、編制、調試一個詞法分析程序,對單詞進行識別和編碼,加深對詞法分析原理的理解。ios
2、設計內容序中的全部單詞進行分類,指出其所屬類型,實現簡單的詞法分析操做。git
3、實驗要求
一、容許用戶本身輸入程序並保存爲文件
二、系統可以輸出通過預處理後的源程序(去掉註釋、換行、空格等)
三、可以將該源程序中全部的單詞根據其所屬類型(整數、保留字、運算符、標識符等。定義的類
C 語言中的標識符只能以字母或下劃線開頭)進行歸類顯示,例如:識別保留字:if、int、for、while、
do、return、break、continue 等,其餘的都識別爲標識符;常數爲無符號整形數;運算符包括:+、-、
*、/、=、>、<、>=、<=、!=等;分隔符包括:,、;、{、}、(、)等。算法
四、實現文件的讀取操做,而不是將文本以字符串形式預存於程序中。文本內容爲待分析的類C 語
言程序。數組
解決方案:函數
關於實驗要求1:主要涉及的是文件的寫入和保存,寫入和保存的內容是用戶輸入的程序。怎麼處理的?個人方法是在這部分寫兩個函數,一個函數用來輸入程序:,另外一個函數用來保存文件。測試
關於實驗要求2:一個函數,用來清除文本信息中的空格 換行 Tab,同時調用該函數後,將通過預處理的內容保存下來,我是在本地又新建了一個文本文件。編碼
關於實驗要求3:這部分是整個實驗的關鍵部分,涉及到對預處理文本的處理。關於這部分的算法思路,在編譯原理書上有提到,可是使用Pascal語言寫的,可是思想是基本不變的,理解下就比較好些了。我想提的一點是,關於RETRACT函數,即實現「將字符數組指針向前移動一個位置」的功能,請查找fseek()函數的使用。spa
關於實驗要求4:文件的讀取函數,readFile()。設計
本身寫的代碼實驗要求基本都實現了,測了幾組也沒bug,不過感受還須要改進,過些日子再貼上來,暫時留這。指針
--------------------------------------------------------------------2015.10.23更新--------------------------------------------------------------------
以前寫的代碼不具有處理註釋的功能,後來加進去了,須要對上述解決方案作下改動,主要是函數和結構,改動以下:
函數說明:
對讀文件函數和寫文件函數、預處理文件函數進行了改動,增長註釋預處理部分dealNote()。
程序運行過程:先由用戶在鍵盤上寫程序,保存文件至G:\\contextfile.txt,對註釋部分進行處理,保存處理後的文件至G:\\nonotecontextfile.txt,再對該文件進行在空格、Tab鍵和空格上的預處理,最後進行judge判斷。
測試數據以下:
/*
}
*/
代碼:
#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] = {'\0'}; 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=='\n') { 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 == '\n' || CHAR == '\t') { 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 = " \t\n"; //刪除空格或者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]='\0'; 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 == '\n' || CHAR == '\t') { 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]['\n'] = 7; fsm[3]['*'] = 4; fsm[4]['/'] = 7; fsm[4]['*'] = 4; fsm[5]['"'] = 0; fsm[5]['\\'] = 6; fsm[7]['/'] = 1; fsm[7]['"'] = 5; }