編譯原理實驗二:詞法分析程序

實驗目的

學習和掌握詞法分析程序手工構造狀態圖及其代碼實現方法。

 

實驗任務

  1. 閱讀已有編譯器的經典詞法分析源程序;
  2. 用C或C++語言編寫一門語言的詞法分析器。

 

實驗內容

  1. 閱讀已有編譯器的經典詞法分析源程序。

選擇一個編譯器,如:TINY,其它編譯器也可(需自備源代碼)。閱讀詞法分析源程序,理解詞法分析程序的手工構造方法——狀態圖代碼化。尤其要求對相關函數與重要變量的作用與功能進行稍微詳細的描述。若能加上學習心得則更好。TINY語言請參考《編譯原理及實踐》第2.5節(見壓縮包裏附帶的文檔)。

  1. 確定今後其他實驗中要設計編譯器的語言,如TINY語言,又如更復雜的C-語言(其定義在《編譯原理及實踐》附錄A中)。也可選擇其它語言,不過要有該語言的詳細定義(可仿照C-語言)。一旦選定,不能更改,因爲要在以後繼續實現編譯器的其它部分。鼓勵自己定義一門語言。
  2. 根據該語言的關鍵詞和識別的詞法單元以及註釋等,確定關鍵字表,畫出所有詞法單元和註釋對應的DFA圖。
  3. 仿照前面學習的詞法分析器,編寫選定語言的詞法分析器。
  4. 準備2~3個測試用例,要求包含正例和反例,測試編譯結果。

 

提示

在充分理解狀態轉換圖代碼化思想的基礎上,思考不同的程序設計語言從詞法角度有什麼區別,可利用增量編程的思想提高編程效率。實驗通過測試後,按規定時間上交源代碼、測試樣例、輸出文件(如有輸出文件)和電子版實驗報告。

 

狀態轉換圖

 

代碼實現

#include<iostream>
#include<fstream>
#include<string.h>
#include<assert.h>
using namespace std;
int LINE=1;   //用於記錄代碼的行數 
bool flag=false;   //用於表示當前註釋是否結束 

typedef enum 
{
    ENDFILE,ERROR,   //文件讀取完畢和錯誤 
    IF,ELSE,INT,RETURN,VOID,WHILE,   //六關鍵字 
    ID,NUM,   //數字和字母 
    ADD,SUB,MUL,DIV,L,LE,G,GE,ISE,NT,NTE,FZ,FH,DH,LXK,RXK,LZK,RZK,LDK,RDK,LZS,RZS   // + - * / < <= > >= == != = ; , ( ) [ ] { } /* */
}TokenType;

typedef enum
{
    START,INNUM,INID,INLT,INGT,INEQ,INNOT,INCOMMENT,INDELETE,READDELETE,DONE
	//開始、數字、字符、小於等於、大於等於、賦值或等於、註釋開始,註釋內容開始,註釋內容結束,完成和單個字符直接完成 
}TokenState;

bool isnum(char a)  //檢測當前字符是否爲數字 
{
	if(a>='0'&&a<='9')
	return true;
	return false;
}

bool isalpha(char a)   //檢測當前字符是否爲字母 
{
	if((a>='a'&&a<='z')||(a>='A'&&a<='Z'))
	return true;
	return false;
}

void PrintToken(TokenType token,const string TokenString)   //對於每一種狀態輸出每一樣的詞法分類結果 
{
	if(TokenString!="\0")   //如果是空行,那麼跳過,保持格式 
	cout<<"\t"<<LINE<<":";
	switch(token)   //根據當前狀態輸出應當匹配的東西 
    {
	    case IF:
        case INT:
        case ELSE:
        case RETURN:
        case VOID:
        case WHILE:
        cout<<"reserved word:"<<TokenString<<endl;   //六個預留詞都是單獨的狀態 
		break;
        case ADD: printf("+\n");  break;
        case SUB: printf("-\n");  break;
        case MUL: printf("*\n");  break;
        case DIV: printf("/\n");  break;
        case L:   printf("<\n");  break;
        case LE:  printf("<=\n"); break;
        case G:   printf(">\n");  break;
        case GE:  printf(">=\n"); break;
        case ISE: printf("==\n"); break;
        case NT:  printf("!\n");  break;
        case NTE: printf("!=\n"); break;
        case FZ:  printf("=\n");  break;
        case FH:  printf(";\n");  break;
        case DH:  printf(",\n");  break;
        case LXK: printf("(\n");  break;
        case RXK: printf(")\n");  break;
        case LZK: printf("[\n");  break;
        case RZK: printf("]\n");  break;
        case LDK: printf("{\n");  break;
        case RDK: printf("}\n");  break;
        case ENDFILE: if(TokenString!="\0") cout<<"COMMENT:"<<TokenString<<endl;break;   //ENDFILE作爲註釋結束的輸出,直接輸出整個字符串 
		case NUM:
	        cout<<"NUM, val="<<TokenString<<endl; break;
        case ID:
	        cout<<"ID, name="<<TokenString<<endl; break;
        case ERROR:   //如果輸入符合文法,那麼不會出現這種狀態 
	        cout<<"ERROR:"<<TokenString<<endl; break;
	}
}

TokenType Find(string a)   //預留詞也是特殊的ID,因此當匹配到ID時,應當檢測它有沒有可能是預留詞 
{
	if(a=="if")     return IF;
	else if(a=="else")   return ELSE;
	else if(a=="int")    return INT;
	else if(a=="return") return RETURN;
	else if(a=="void")   return VOID;
	else if(a=="while")  return WHILE;
	else return ID;
}

void getToken(string tmp)   //狀態轉換函數 
{
	bool save=true;   //是否將當前字符存入匹配單詞字符 
	TokenType currentToken;   //當前的字符 
	TokenState state;   //當前的狀態 
	string tokenString="";   //用於存放待匹配的單詞 
	if(flag==false)   //如果當前還處在註釋內部 
	state=START;
	else
	state=INDELETE;   //那麼繼續返回註釋狀態 
	for(int i=0;i<=tmp.length();i++)   //按照行爲單位讀入 
	{
		save=true;
		switch(state)   //判斷當前狀態 
		{
			case START:   //開始狀態下,之後狀態參考DFA圖片! 
				if(isnum(tmp[i]))
				state=INNUM;
				else if(isalpha(tmp[i]))
				state=INID;
				else if(tmp[i]=='<')
				state=INLT;
				else if(tmp[i]=='>')
				state=INGT;
				else if(tmp[i]=='=')
				state=INEQ;
				else if(tmp[i]=='!')
				state=INNOT;
				else if(tmp[i]==' '||tmp[i]=='\t'||tmp[i]=='\n')
				save=false;
				else if(tmp[i]=='/')
				state=INCOMMENT;
				else
                {
                	state=DONE;
                    switch(tmp[i])
                    {
					    case '\0':   //讀取註釋到了最後,仍然沒有結束,此時返回特殊的狀態 
                            save=false;
                            currentToken=ENDFILE;
                            break;
                        case '+':
                            currentToken=ADD;
                            break;
                        case '-':
                            currentToken=SUB;
                            break;
                        case '*':
                            currentToken=MUL;
                            break;
                        case '(':
                            currentToken=LXK;
                            break;
                        case ')':
                            currentToken=RXK;
                            break;
                        case '[':
                            currentToken=LZK;
                            break;
                        case ']':
                            currentToken=RZK;
                            break;
                        case '{':
                            currentToken=LDK;
                            break;
                        case '}':
                            currentToken=RDK;
                            break;
                        case ';':
                            currentToken=FH;
                            break;
                        case ',':
                            currentToken=DH;
                            break;
                        default:
						    currentToken=ERROR;
							break; 
                    }
                }
            break;
			case INCOMMENT:   //將進入註釋狀態的時候 
				if(tmp[i]!='*')
				{
                    save=true;   //如果是連續的/*,那麼進入註釋,否則只有一個/,返回除法 
                    state=DONE;
                    i--;
                    currentToken=DIV;
		        }
                else
                {
				    state=INDELETE;
				    flag=true;
				}
				break;
			case INDELETE:
				save=true;
			    if(tmp[i]=='*')
				state=READDELETE;   //正式進入註釋,如果有一個*,進入到將要退出註釋的狀態 
       	        if(tmp[i]=='\0')
                {
				    state=DONE;
                    currentToken=ENDFILE;   //如果當前行所有都已經結束,那麼直接把所有的字符給到註釋 
                }
       	        break;
       	    case READDELETE:   //將要退出註釋的狀態,此時如果再來一個/,退出註釋,否則返回正式註釋狀態 
       	        if(tmp[i]=='/')
       	        {
				    state=DONE;
				    flag=false;
				    currentToken=ENDFILE;
				}
       	        else
       	        {
				    i--;
				    state=INDELETE;
				    save=false;
				}
       	        break;
			case INLT:  //<和<=第一個字符都是<,=和==第一個字符都是=,>和>=第一個字符都是>,!和!=第一個字符都是!,下面4種狀態,如果沒有匹配到第二個“=”,需要退回一個字符 
				state=DONE;
				if(tmp[i]=='=')
				currentToken=LE;
				else
				{
					save=false;
					i--;
					currentToken=L;
				}
				break;
			case INGT:  
				state=DONE;
				if(tmp[i]=='=')
				currentToken=GE;
				else
				{
					save=false;
					i--;
					currentToken=G;
				}
				break;
			case INEQ:
			    state=DONE;
				if(tmp[i]=='=')
				currentToken=ISE;
				else
				{
					save=false;
					i--;
				    currentToken=FZ;
				}
				break;
			case INNOT:
				state=DONE;
				if(tmp[i]=='=')
				currentToken=NTE;
				else
				{
					save=false;
					i--;
					currentToken=NT;
				}
				break;
			case INNUM:   //數字和字母狀態,只要接下來不能連續匹配到數字和字母,那麼就會停止匹配 
				if(!isdigit(tmp[i]))
                {
                	i--;
                    save=false;
                    state=DONE;
                    currentToken=NUM;
                }
                break;
			case INID:
				if(!isalpha(tmp[i]))
                {
                	i--;
                    save=false;
                    state=DONE;
                    currentToken=ID;
                }
                break;
			case DONE:break;
			default:
				state=DONE;
				currentToken=ERROR;
				break;
		}
		if(save && tmp[i]!='\0')
		tokenString+=tmp[i];   //保存好當前的字符,加入匹配字符串 
		if(currentToken==ID)   //如果是ID類型,那麼找一找有沒有可能會是預留類 
		currentToken=Find(tokenString);
		if(state==DONE)   //如果匹配完畢 
		{
			PrintToken(currentToken,tokenString);   //輸出! 
			tokenString="";   //匹配字符串歸零,開始下一輪 
			if(flag==false)   //flag表示是否在註釋內,如果當前註釋還沒結束,需要退回到註釋內部狀態 
			state=START;
			else
			state=INDELETE;
		}
	}
	LINE++;
}

void read()   //從lab2.txt讀取數據 
{
	fstream ff("lab2.txt",ios::in);
	assert(ff.is_open());
    string tmp;
    cout<<"C MINUS COMPILATION:lab2.txt"<<endl;
	while(!ff.eof())
	{
		getline(ff,tmp);
		{
			cout<<endl;
		    cout<<"LINE"<<LINE<<":"<<tmp;
		    if(!ff.eof())
		    cout<<endl;
		}
		if(ff.eof())
		cout<<"EOF"<<endl;
		getToken(tmp);
	}
}

int main(int argc,char* argv[])
{
	read();
	return 0;
}

 

發佈了35 篇原創文章 · 獲贊 43 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章