【編譯原理】類C語言詞法分析器的設計

1.實驗要求ios

輸入爲一個以類C語言編寫的源程序spa

輸出爲一組二元組序列構成的文本文件,一行爲一個二元組,二元組中間以逗號隔開設計

實驗報告上要求附上DFA   code

2.語言說明:orm

保留字:unsigned、break、return、void、case、float、char、for、while、continue、if、default、do、int、switch、double、long、elseblog

運算符:+,-,*,/,>,>=,<,<=,==,!=,&&,||,!token

界限符:{ }( ); ,ip

常量:十進制無符號數ci

標識符:以字母或下劃線開始,後面跟上字母或數字字符串

3.   實驗原理

詞法分析是編譯的第一階段。詞法分析器的主要任務是讀入源程序的輸入字符,將它們組成詞素,生成並輸出一個詞法單元序列,這個詞法單元序列被輸出到語法分析器進行語法分析。另外,因爲詞法分析器在編譯器中負責讀取源程序,所以除了識別詞素以外,它還會完成一些其餘任務,好比過濾掉源程序中的註釋和空白,將編譯器生成的錯誤消息與源程序的位置關聯起來等。詞法分析器的做用以下: 

讀入源程序的輸入字符,將它們組成詞素,生成並輸出一個詞法單元序列; 

過濾掉源程序中的註釋和空白; 

將編譯器生成的錯誤消息與源程序的位置關聯起來; 

4.DFA設計:

5.實驗代碼

#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<iomanip>
#include<ctype.h>
#include<fstream>
using namespace std;
typedef pair<int,int> mp;
const string key_word[]={"unsigned","break","return","void","case","float","char","for","while","continue","if","default","do","int","switch","double","long","else"};//保留字
const string operators[]={"+","-","*","/",">",">=","<","<=","==","!=","&&","||","!"};//運算符
const char jiefu[]={',',';','(',')','{','}'};//界符
map<string,mp> flag_table;//標識符表等
map<string,mp>num_table;//常量表
map<string,mp>str_table;//字符串表
map<string,mp>head_table;//頭文件表
map<string,mp>char_table;//字符表
map<char,mp>fenjiefu_table;//分界符表
int keymark[40],operatormark[40];
int isjiefu(char ch)
{
	for(int i=0;i<5;i++)
		if(ch==jiefu[i])
			return 1;
	return 0;
}
//判斷是否是關鍵字
int iskey(string ch)
{
	int i;
	for(i=0;i<17;i++)
		if(key_word[i]==ch)
			return i;
	return -1;
}
int isope(string ch)
{
	int i;
	for(i=0;i<12;i++)
		if(operators[i]==ch)
			return i;
	return -1;
}
int ischar(char ch)
{
	if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z')
		return 1;
	return -1;
}
int isnumber(char ch)
{
	if(ch>='0'&&ch<='9')
		return 1;
	return -1;
}
//得到關鍵字或者標識符
void get_keyflag(char* ptr,FILE* f)
{
	string token;
	token+=*ptr;
	while(1)
	{
		*(++ptr)=fgetc(f);
		if(ischar(*ptr)==-1&&isnumber(*ptr)==-1&&*ptr!='_')
		break;
		token+=*ptr;
	}
	ungetc(*ptr,f);
	//*ptr='\0';
	int h=iskey(token);
	if(h>=0)//是關鍵字
	{
		if(!keymark[h])
			keymark[h]=1;
		cout<<token<<" , "<<"保留字"<<endl;
	}
	else//標識符
	{
		mp tmp;
		if(flag_table.find(token)==flag_table.end())//嶄新的標記符
		{
			tmp=make_pair(1,flag_table.size()+1);
			flag_table[token]=tmp;
		}
		else
		{
			map<string,mp>::iterator it;
			it=flag_table.find(token);
			tmp=it->second;
		}
		cout<<token<<" , "<<"標識符"<<endl;
	}
}
//獲取數字
void get_num(char* ptr,FILE* f)
{
	string token;
	token+=*ptr;
	while(1)
	{
		*(++ptr)=fgetc(f);
		if(isnumber(*ptr)!=1)
		break;
		token+=*ptr;
	}
	ungetc(*ptr, f);
	mp tmp;
	if(num_table.find(token)==num_table.end())//新的數字
	{
		tmp=make_pair(2,num_table.size()+1);//2表明着數字
		num_table[token]=tmp;
	}
	else
	{
		map<string,mp>::iterator it;
		it=num_table.find(token);
		tmp=it->second;
	}
	cout<<token<<" , "<<"常量"<<">"<<endl;
}
//得到字符串
void get_string(char* ptr,FILE* f)
{
	string token;
	*(++ptr)=fgetc(f);
	token+=*(ptr);
	while(1)
	{
		*(++ptr)=fgetc(f);
		if(feof(f)||*ptr=='"')
		break;
		token+=*(ptr);
	}
	if(*(ptr)=='"')
	{
		map<string,mp>::iterator it;
		it=str_table.find(token);
		mp tmp;
		if(it==str_table.end())//新的字符串
		{

			tmp=make_pair(3,str_table.size()+1);
			str_table[token]=tmp;
		}
		else
		{tmp=it->second;}
		cout<<token<<" , "<<"標識符"<<endl;
	}
	//找不到匹配的字符串
	else
	{
		cout<<"程序錯誤!"<<endl;
	}
}
void double_operator(char* ptr,FILE* f)
{
	string token,tmp;
	token+=*ptr;
	tmp+=*ptr;
	*(++ptr)=fgetc(f);
	token+=*ptr;
	int p=isope(token);
	if(p>=0)
	{
		operatormark[p]=1;
		cout<<token<<" , "<<"運算符"<<endl;
	}
	else
	{
		ungetc(*ptr,f);
		p=isope(tmp);
		operatormark[p]=1;
		cout<<tmp<<" , "<<"運算符"<<endl;
	}
}
void single_operator(char* ptr,FILE* f)
{
	string token;
	token+=*ptr;
	int index=isope(token);
	if(index>=0)
	{
		operatormark[index]=1;
		cout<<token<<" , "<<"運算符"<<endl;
	}
	else
	{
		if(*ptr<0||*ptr>127)
			cout<<"輸入的不是ascii碼"<<endl;
	}
}
void acehandle(char* ptr,FILE* f)
{
   string token;
   token+=*ptr;
   while(1)
   {
	   ptr++;
	   *ptr=fgetc(f);
	   if(*ptr==' '||*ptr=='"'||*ptr=='<')
   	    break;
	   token+=*ptr;
   }
   if(*ptr=='<'||*ptr=='"')
   ungetc(*ptr,f);
   if(token=="#define")
   {
	   cout<<token<<","<<"宏定義"<<endl;
   }
   if(token=="#include")
   {
	   cout<<setw(5)<<"<"<<setw(10)<<token<<setw(20)<<"預處理"<<setw(20)<<">"<<endl;
	   *ptr=fgetc(f);
	   cout<<setw(5)<<"<"<<setw(10)<<*ptr<<setw(20)<<"分界符"<<setw(20)<<">"<<endl;
	   string tmp;
	   *ptr=fgetc(f);
	   tmp+=*ptr;
	   while(1)
	   {
		   *(++ptr)=fgetc(f);
		   if(*ptr=='>'||*ptr=='"')
			   break;
		   tmp+=*ptr;
	   }
	   mp p;
	   if(head_table.find(tmp)==head_table.end())
	   {
		   p=make_pair(4,head_table.size()+1);
		   head_table[tmp]=p;
	   }
	   else
	   {
		   map<string,mp>::iterator it;
		   it=head_table.find(tmp);
		   p=it->second;
	   }
	   cout<<setw(5)<<"<"<<setw(10)<<tmp<<setw(10)<<p.first<<setw(10)<<"頭文件"<<setw(10)<<p.second<<">"<<endl;
	   cout<<setw(5)<<"<"<<setw(10)<<*ptr<<setw(20)<<"分界符"<<setw(20)<<">"<<endl;
   }
}
void handlechar(char ch,FILE* f)
{
	char* ptr=new char[1000];
	*ptr=ch;
	if(ischar(*ptr)==1)//以字母開頭的--關鍵字或者標識符
	{
		get_keyflag(ptr,f);}
	else if(isnumber(*ptr)==1)
	{
		get_num(ptr,f);}
	else if(*ptr=='#')
	{
		acehandle(ptr,f);}
	else if(*ptr=='"')
	{
		get_string(ptr,f);}
	else if(*ptr=='+'||*ptr=='-'||*ptr=='*'||*ptr=='/'||*ptr=='='||*ptr=='%'||*ptr=='>'||*ptr=='<')
	{
		double_operator(ptr,f);}
	else if(isjiefu(*ptr)==1)
	{
		mp tmp;
		map<char,mp>::iterator it;
		if(fenjiefu_table.find(*ptr)==fenjiefu_table.end())
		{
			tmp=make_pair(6,fenjiefu_table.size()+1);
			fenjiefu_table[*ptr]=tmp;}
		else
		{
			it=fenjiefu_table.find(*ptr);
			tmp=it->second;}
		 cout<<*ptr<<" , "<<"界符"<<endl;
	}
	else
	{
		single_operator(ptr,f);}
}
int main()
{
	FILE* f;
	f=fopen("input.txt","r");

	if(f==NULL)
	{
		cout<<"讀入文件錯誤"<<endl;
		return 0;}
	else
	{
		char ch=fgetc(f);
		while(!feof(f))
		{
			if(ch!=' '&&ch!='\n'&&ch!='\t')
				handlechar(ch,f);
			ch=fgetc(f);}
	}
	fclose(f);
	return 0;
}

輸入的類C詞法

char word[10];
        char pro[100][100] = { "PROGRAM", 

"BEGIN", "END", "VAR", "INTEGER", "WHILE", "IF", "THEN", 

"ELSE", "DO", "PROCEDURE" ,


"char","int","if","else","var" 

,"return","break","do","while","for","double","float","short"};

        int n = 0;
        word[n++] = a[i++];
        while ((a[i] >= 'A'&&a[i] <= 'Z') || (a

[i] >= '0' && a[i] <= '9')||(a[i]>='a'&&a[i]<='z'))
        {
            word[n++] = a[i++];
        }
        word[n] = '\0';
        i--;

程序運行輸出結果:

6.分析討論:

(1)碰見問題:二目運算符沒法識別 ,致使拆分識別爲兩個單目運算符;第二個問題就是若是識別的詞法帶有註釋,會報錯。

(2)解決方法: 修改識別運算符代碼部分加入雙目運算符的識別

(3)後續改進思路:一個不方便的地方就是沒能實現經過輸入文件名讀取文件的數據,每次讀入新的文件仍須要修改代碼。