設計、編制、調試一個詞法分析程序,對單詞進行識別和編碼,加深對詞法分析原理的理解。
二、設計內容設計並實現一個詞法分析器,實現對指定位置的類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] = {'\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;
}
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
