C語言實現LL(1)文法分析器的構造

實驗任務:構造一個LL(1)文法分析器
思路是:
1.用map<char,int>逐個對終結符與非終結符標識
2.把產生式壓棧
3.得到Fisrt和Follow集
4.構建預測分析表
5.分析輸入的合法性

整體代碼如下

#include<iostream>
#include<stdio.h>
#include<vector>
#include<string>
#include<stack>
#include<map>
#include<cstring>
#include<cstdlib>
#include <bits/ios_base.h>
using namespace std;
map<char,int>getnum;
vector<char>getzf;  
vector<string>proce(10);
vector<string>first(20);
vector<string>follow(20);
int table[100][100];      //預測分析表
int num;
int numv;//終結符的數量-1
char j[2];
void  read()//讀終結符、非終結符、產生式 
{
	char c;
	int i=0;
	int n=0;
	cout<<"輸入產生式集合(空字用‘@’表示),輸入一條後換行,以‘end’結束:"<<endl;
	string ss;
	string dd;
	int j=0;
	int y=0;
	while(cin>>ss&&ss!="end")
	{		 
		dd.clear();
		dd+=ss[0];
		proce[j]+=dd;
		for(i=3;i<ss.length();i++)
		{	
		if(ss[i]!='|'){
			dd.clear();
			dd+=ss[i];
			proce[j]+=dd;
		} 
			else 
		{
			dd.clear();
			dd+=ss[0];
			dd+=ss[++i];
			proce[++j]+=dd;
			}	
	}
	j++;
}

	getnum['#']=0;//#代表結束標誌 
	//	getzf[0]='#';沒有定義數組大小的時候這樣輸入是錯誤的 
	getzf.push_back('#');
	//終結符壓棧 
	for(int i=0;i<proce.size();i++)
	{
	for(int k=0;k<proce[i].length();k++)
	{
	if(proce[i][k]!='-'&&proce[i][k]!='|')
	{
	if(proce[i][k]<64||proce[i][k]>90)
	{
	 	 for( y=0;y<getzf.size();y++)
	 	{
		 	if(proce[i][k]==getzf[y]) break;
		 }
		 if(y==getzf.size()&&k!=2){//這裏讓k!=2是不讓第三位置的>進去 
		getnum[proce[i][k]]=++n;
		getzf.push_back(proce[i][k]);
		}
	}
	}	  
}
}
	 getnum['@']=++n;
	 numv=n;//終結符的數量等於當前n的值 
	 getzf.push_back('@');
	//非終結符壓棧 
	for(int i=0;i<proce.size();i++)
	{
	for(int k=0;k<proce[i].length();k++)
	{
	if(proce[i][k]!='-'&&proce[i][k]!='|'&&proce[i][k]!='>')
	{
	if(proce[i][k]>64&&proce[i][k]<91)
	{
	 	 for( y=0;y<getzf.size();y++)
	 	{
		 	if(proce[i][k]==getzf[y]) break;
		 }
		 if(y==getzf.size()){
		getnum[proce[i][k]]=++n;
			num=n;//終結符加非終結符的數量等於當前i的值 
		getzf.push_back(proce[i][k]);
		}
	}
	}	  
}
}
}
	
void get_firstT()//給終結符的first數組賦值
{
	int i;//不能在下面int 
	//先給終結符的first數組賦值
	for( i=1;i<=numv;i++) 
	{
			itoa(i,j,10);
    		first[i]=j;//之前寫的是first[i].push_back(j[0])是錯的,字符串數組的輸入不需要加下標,且如果是j[0]一個字符不能裝到一個字符串當中去 
	}
}

string get_firstF(int *a)//給非終結符的first數組賦值
{//犯了一個錯誤,下面的a沒有加* 
	for(int i=0;i<proce.size();i++)
	{
		if(getnum[proce[i][0]]==*a)
		{
	   		if(getnum[proce[i][1]]<=numv)
			{
				itoa(getnum[proce[i][1]],j,10); 
				first[*a]+=j;
			}
			else
			{ 
				//first[getnum[proce[i][0]]].clear();
				first[getnum[proce[i][0]]]=get_firstF(&getnum[proce[i][1]]);
			}
	} 	
	}
	return first[*a]; 
} 

void  get_follow(int *a){//得到follow集 
//犯了一個錯誤,以stirng開頭但是沒有返回值 
	int i,j1;
	int flag=0;
	for(i=0;i<proce.size();i++)
	{
		for(j1=1;j1<proce[i].length();j1++)
		{
			if(getnum[proce[i][j1]]==*a)//這地方應該是j1我寫成了k 
			{
			if(j1==proce[i].length()-1)
				{
				if(getnum[proce[i][j1]]!=getnum[proce[i][0]])
				follow[*a]+=follow[getnum[proce[i][0]]]; 
			}
			else 
			{
			if(getnum[proce[i][j1+1]]<=numv)	
				{
						itoa(getnum[proce[i][j1+1]],j,10);
						follow[*a]+=j;
				}
			else
				{
					for(int jj=0;jj<first[getnum[proce[i][j1+1]]].size();jj++) 
					{
						if(atoi(&first[getnum[proce[i][j1+1]]][jj])==numv)//等於空時
						follow[*a]+=follow[getnum[proce[i][0]]];
						else
						follow[*a]+=first[getnum[proce[i][j1+1]]][jj];
					}
					flag=1;//標誌位,如果已經找到*a後面的非終結符就可以停止了 
			}
		}
		}
		 } 
		 
		 	if(flag==1) break; //停止尋找 
	}
}

void get_table()          //得預測分析表
{
	memset(table,-1,sizeof(table));//剛開始tableM沒有初始化,導致本該是空格的地方出現E->TA 
   for(int i=0;i<proce.size();i++)   //掃所有產生式
   {
       if(proce[i][1]=='@')     //直接推出空字的,特判下(follow集=產生式左邊的vn中元素填)
          {
             string flw=follow[getnum[proce[i][0]]];
             for(int k=0;k<flw.size();k++)
             {
               table[getnum[proce[i][0]]][flw[k]-'0']=i;
             }
          }
       string temps=first[getnum[proce[i][1]]];
       for(int j=0;j<temps.size();j++)               //考察first集
       {
       	if(atoi(&temps[j])!=numv) 
       	{
               // table[getnum[proce[i][0]]][atoi(&temps[j])]=i;//atoi不能這麼用,他表示的是從當前位一直到末位 
              table[getnum[proce[i][0]]][temps[j]-'0']=i;
           }
           else                                     //有空字的,考察follw集
           {
               string flw=follow[getnum[proce[i][1]]];
              for(int k=0;k<flw.size();k++)
             {
                 table[getnum[proce[i][0]]][flw[k]-'0']=i;
             }
           }
       }
   }
}

string get_proce(int i)  //由對應下標獲得對應產生式
{
    if(i<0)return " ";    //無該產生式
    string ss;
    ss+=proce[i][0];
    ss+="->";		//把->要加上 
    for(int j=1;j<proce[i].size();j++)
       ss+=proce[i][j];
   return ss;
}

void print_table()//輸出預測分析表 
{
    cout<<"預測分析表:"<<endl;
   for(int i=0;i<numv;i++)
      cout<<'\t'<<getzf[i];
      cout<<endl;
   for(int i=numv+1;i<=num;i++)
    {
    	cout<<endl<<"________________________________________________________"<<endl;
       cout<<getzf[i];
       for(int j=0;j<numv;j++)
       {
           cout<<'\t'<<get_proce(table[i][j])<<"";
       }
    }
    	cout<<endl<<"________________________________________________________"<<endl;
     cout<<endl;
}

string word;
bool analyze()       //分析word的合法性,若合法,輸出所有產生式
{
    stack<char>sta;
    sta.push('#');  //#最先進棧 
	sta.push(proce[0][0]);
    int i=0;
    while(!sta.empty())
    {
       int cur=sta.top();
       sta.pop();
       if(cur==word[i])       //是終結符的話找下一個 
       {
           i++;
       }
       else  if(cur=='#')   //遇到#結束
       {
           return 1;
       }
       else  if(table[getnum[cur]][getnum[word[i]]]!=-1) //查表
       {
 
           int k=table[getnum[cur]][getnum[word[i]]];
           cout<<proce[k][0]<<"->";
          for(int j=1;j<proce[k].size();j++)
                 cout<<proce[k][j];
           cout<<endl;
           for(int j=proce[k].size()-1;j>0;j--)  //逆序入棧
                {  if(proce[k][j]!='@')
                   sta.push(proce[k][j]);
                }
       }
       else     
       {
           return 0;
       }
    }
    return 1;
}

void scanf()   //輸入字
{
cout<<"請輸入字:"<<endl;
    cin>>word;
    if(analyze())
        cout<<"該字有效,所用產生式如上!"<<endl;
    else   
	cout<<"出錯了!"<<endl;
}

int main()
{
	int k;
	int j;
	read();
	//終結符的first集 
	get_firstT();
	//非終結符的first集 
	for(k=numv+1;k<=num;k++) //犯了一個錯誤,numv的位置寫成7 
	{
		get_firstF(&k);
	}

		follow[numv+1]+='0'; //這地方加的是0而不是# 
		for(k=numv+1;k<=num;k++) //犯了一個錯誤,numv的位置寫成7 
	{
		 get_follow(&k);
	}
     cout<<endl;  
     get_table();
     print_table();
     scanf();	
     return 0;
}

一個LL(1)文法的例子:

E->TA
A->+TA|@
T->FB
B->FB|@
F-|i
(這裏面本是(E),但出錯,換成就可以了,不知道爲什麼)
在這裏插入圖片描述
輸入i+i
i
在這裏插入圖片描述

例二:
S->iEtSB|a
B->eS|@
E->b
在這裏插入圖片描述例三:
S->|a
L->SB
B->,SB|@
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章