簡單語法分析器

詞法分析

1、 待分析的簡單語言的詞法

(1) 關鍵字:(所有關鍵字都是小寫。)

begin  if   then   while   do   end

(2) 運算符和界符:

:=   +   –   *   /   <   <=   <>   >   >=   =   ;   (   )   #

(3) 其他單詞是標識符(ID)和整型常數(NUM),通過以下正規式定義:

ID=letterletter| digit* 

NUM=digit digit * 

(4) 空格由空白、製表符和換行符組成。空格一般用來分隔IDNUM,運算符、界符和關鍵字,詞法分析階段通常被忽略。

2、 各種單詞符號對應的種別碼

單詞符號

種別碼

單詞符號

種別碼

begin

1

:

17

if

2

:=

18

then

3

>

20

while

4

<>

21

do

5

<=

22

end

6

<

23

letter(letter| digit)*

10

>=

24

digit digit *

11

=

25

*

13

;

26

/

14

(

27

+

15

)

28

-

16

#

0

3、匹配字符流程 

依照狀態轉換圖,進行匹配:

對單一字符,直接比較;

對關鍵字,進行循環匹配(0-1-2);

對數字,進行循環匹配(0-3-4)。

4詞法分析程序的功能

輸入:所給文法的源程序字符串。

輸出:二元組(syntoken/sum)構成的序列。

其中:syn爲單詞種別碼;

token爲存放的單詞自身字符串;

sum爲整型常數。

錯誤:返回row行數

 

5、設計思路

設計scaner,查詢當前所指字符,並與表格內字符進行比較匹配:

匹配成功,返回相應代碼號;

匹配失敗,則彈出錯誤信息(用row記錄當前處理的行數)。


6、源程序代碼:

#include<stdio.h>
#include<string.h>
#include<iostream.h>

char prog[80],token[8];
char ch;
int syn,p,m=0,n,row,sum=0;
char *rwtab[6]={"begin","if","then","while","do","end"};
//匹配字符
void scaner()
{
	for(n=0;n<8;n++) token[n]=NULL;
	ch=prog[p++];
	while(ch==' ')
	{
		ch=prog[p];
		p++;
	}
	if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
	{
		m=0;
		while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
		{
			token[m++]=ch;
			ch=prog[p++];
		}
		token[m++]='\0';
		p--;
		syn=10;
		for(n=0;n<6;n++)
			if(strcmp(token,rwtab[n])==0)
			{
				syn=n+1;
				break;
			}
	}
	else if((ch>='0'&&ch<='9'))
	{
		{
			sum=0;
			while((ch>='0'&&ch<='9'))
			{
				sum=sum*10+ch-'0';
				ch=prog[p++];
			}
		}
		p--;
		syn=11;
		if(sum>32767)
			syn=-1;
	}
	else switch(ch)
	{
case'<':m=0;token[m++]=ch;
	ch=prog[p++];
	if(ch=='>')
	{
		syn=21;
		token[m++]=ch;
	}
	else if(ch=='=')
	{
		syn=22;
		token[m++]=ch;
	}
	else
	{
		syn=23;
		p--;
	}
	break;
case'>':m=0;token[m++]=ch;
	ch=prog[p++];
	if(ch=='=')
	{
		syn=24;
		token[m++]=ch;
	}
	else
	{
		syn=20;
		p--;
	}
	break;
case':':m=0;token[m++]=ch;
	ch=prog[p++];
	if(ch=='=')
	{
		syn=18;
		token[m++]=ch;
	}
	else
	{
		syn=17;
		p--;
	}	break;
case'*':syn=13;token[0]=ch;break;
case'/':syn=14;token[0]=ch;break;
case'+':syn=15;token[0]=ch;break;
case'-':syn=16;token[0]=ch;break;
case'=':syn=25;token[0]=ch;break;
case';':syn=26;token[0]=ch;break;
case'(':syn=27;token[0]=ch;break;
case')':syn=28;token[0]=ch;break;
case'#':syn=0;token[0]=ch;break;
case'\n':syn=-2;break;
default: syn=-1;break;
}
}
//主程序讀入 循環
void main()
{
	p=0;
	row=1;
	cout<<"Please input string:"<<endl;
	do
	{
		cin.get(ch);
		prog[p++]=ch;
	}
	while(ch!='#');
	p=0;
	do
	{
		scaner();
		switch(syn)
		{
		case 11: cout<<"("<<syn<<","<<sum<<")"<<endl; break;  
	    case -1: cout<<"Error in row "<<row<<"!"<<endl; break;
		case -2: row=row++;break;
        default: cout<<"("<<syn<<","<<token<<")"<<endl;break;
		}
	}
	while (syn!=0);
}


7、 結果驗證

begin y:=11 if y<13 then y:1+2*3+y/2; end#

 

 源程序(包括上式未有的whiledo以及判斷錯誤語句):

begin

x<&

while

h<0

do

b>9

k>9-x

end

#

 

 $ 字符不在表內,所以匹配錯誤,返回錯誤行數。


語法分析

1、分析文法

  • 語法表達式

G(E)à E +T | T

       à T* F | F

    à i | (E)

  •  展開表達式

 (1) à E +

 (2) à T

 (3) à T* 

 (4) à F

 (5) à i 

 (6) à (E)

 

  • 消除左遞歸:

G’(E)E->TE'

     E'->+TE'|ε

     T->FT'

     T'->*FT'|ε

     F->(E)|i

2、構造LL(1)分析表

非終結符

輸入符號

i

+

*

#

E

E->TE'

 

 

E->TE'

synch

synch

E’

 

E'->+TE'

 

 

E'->ε

E'->ε

T

T->FT'

synch

 

T->FT'

synch

synch

T’

 

T'->ε

T'->*FT'

 

T'->ε

T'->ε

F

F->i

synch

synch

F->(E)

synch

synch

  •  構造LR分析表

狀態

action

goto

id

+

*

(

)

#

E

T

F

0

S5

 

 

S4

 

 

1

2

3

1

 

S6

 

 

 

acc

 

 

 

2

 

R2

S7

 

R2

R2

 

 

 

3

 

R4

R4

 

R4

R4

 

 

 

4

S5

 

 

S4

 

 

8

2

3

5

 

R6

R6

 

R6

R6

 

 

 

6

S5

 

 

S4

 

 

 

9

3

7

S5

 

 

S4

 

 

 

 

10

8

 

S6

 

S11

 

 

 

 

 

9

 

R1

S7

 

R1

R1

 

 

 

10

 

R3

R3

 

R3

R3

 

 

 

11

 

R5

R5

 

R5

R5

 

 

 

 

  • LL(1)狀態切換

 


3、設計程序

LL(1)——————————————————————————————————

#include <iostream.h>
char inputstream[50];   
int temp=0;      
int right;      
//狀態轉換函數
void e();
void e1();
void t();
void t1();
void f();
//主程序
void main() 
{
	right=1;
	cout<<"請輸入您要分析的字符串以#結束(^爲空字符):"<<endl;
	cin>>inputstream;
	e();
	if((inputstream[temp]=='#')&&right)
	cout<<"分析成功"<<endl;
	else
	cout<<"分析失敗"<<endl;
}

void e()
{
	cout<<"E->TE'"<<endl;
	t();
	e1();
}

void e1()
{
	if(inputstream[temp]=='+')
	{
		cout<<"E'->+TE'"<<endl;
		temp++;
		t();
		e1();
	}
	else
	if (inputstream[temp]!='#'||inputstream[temp]!=')')
	{
		cout<<"T'->^"<<endl;
		return ;
	}
	else
	right=0;
}
void t()
{
	cout<<"T->FT'"<<endl;
	f();
	t1();
}
void t1()
{
	if(inputstream[temp]=='*')
	{
		cout<<"T'->*FT'"<<endl;
		temp++;
		f();
		t1();
	}
	else
	if (inputstream[temp]!='#'&&inputstream[temp]!=')'&&inputstream[temp]!='+')
	{
		cout<<"T'->^"<<endl;
		right=0;
	}
}
void f()
{
	if(inputstream[temp]=='i')
	{
		cout<<"F->i"<<endl;
		temp++;
	}
	else

	if(inputstream[temp]=='(')
	{
		cout<<"F->(E)"<<endl;
		temp++;
		e();
		if(inputstream[temp]==')')
		{
			cout<<"F->(E)"<<endl;
			temp++;
		}
		else
		right=0;
	}
	else right =0;
}
LR——————————————————————

#include<iostream>
#include<stack>
using namespace std;
char inputstream[50];
//棧區 
//符號和狀態
stack<char> symbol;
stack<int> state;
//分析表內容
//符號表
char sym[12][6]={
   {'s','e','e','s','e','e'},
   {'e','s','e','e','e','a'},
   {'r','r','s','r','r','r'},
   {'r','r','r','r','r','r'},
   {'s','e','e','s','e','e'},
   {'r','r','r','r','r','r'},
   {'s','e','e','s','e','e'},
   {'s','e','e','s','e','e'},
   {'e','s','e','e','s','e'},
   {'r','r','s','r','r','r'},
   {'r','r','r','r','r','r'},
   {'r','r','r','r','r','r'}
};
//數字表
char snum[12][6]={
	{5,1,1,4,2,1},
	{3,6,5,3,2,0},
	{2,2,7,2,2,2},
	{4,4,4,4,4,4},
	{5,1,1,4,2,1},
	{6,6,6,6,6,6},
	{5,1,1,4,2,1},
	{5,1,1,4,2,1},
	{3,6,5,3,11,4},
	{1,1,7,1,1,1},
	{3,3,3,3,3,3},
	{5,5,5,5,5,5}
};
//goto表
int go2[12][3]={
	{1,2,3},
	{0,0,0},
	{0,0,0},
	{0,0,0},
	{8,2,3},
	{0,0,0},
	{0,9,3},
	{0,0,10},
	{0,0,0},
	{0,0,0},
	{0,0,0},
	{0,0,0}
};
//action函數[i,a]
void action(int i,char *&a,char &how,int &num,char &A,int &b)
{
	int j;//指向action表列號
	switch(*a)
	{
		case 'i':
		   j=0;break;
		case '+':
		   j=1;break;
		case '*':
		   j=2;break;
		case '(':
		   j=3;break;
		case ')':
		   j=4;break;
		case '#':
		   j=5;break;
		default:
		   j=-1;break;
	}
	if(j!=-1)
	{
	   how=sym[i][j];
	   num=snum[i][j];
	   if(how=='r')
	   {
			switch(num)
			{
				case 1:
				 A='E',b=3;
				 cout<<"按E->E+T規約"<<endl;
				 break;
				case 2:
				 A='E',b=1;
				 cout<<"按E->T規約"<<endl;
				 break;
				case 3:
				 A='T',b=3;
				 cout<<"按T->T*F規約"<<endl;
				 break;
				case 4:
				 A='T',b=1;
				 cout<<"按T->F規約"<<endl;
				 break;
				case 5:
				 A='F',b=3;
				 cout<<"按F->(E)規約"<<endl;
				 break;
				case 6:
				 A='F',b=1;
				 cout<<"按F->id規約"<<endl;
				 break;
				default:
				 break;
			}
	   }
	}
}
//goto[t,A]
int go(int t,char A)
{
	switch(A)
	{
		case 'E':
		   return go2[t][0];break;
		case 'T':
		   return go2[t][1];break;
		case 'F':
		   return go2[t][2];break;
	}
}
//error處理函數
void error(int i,int j,char *&a)
{
	cout<<"error"<<endl;
	switch(j)
	{
		case 1://期望輸入id或左括號,但是碰到+,*,或$,就假設已經輸入id了,轉到狀態5
		   state.push(5);
		   symbol.push('i');//必須有這個,如果假設輸入id的話,符號棧裏必須有....
		   cout<<"缺少運算對象id"<<endl;
		   break;
		case 2://從輸入中刪除右括號
		   a++;
		   cout<<"不配對的右括號"<<endl;
		   break;
		case 3://期望碰到+,但是輸入id或左括號,假設已經輸入算符+,轉到狀態6
		   state.push(6);
		   symbol.push('+');
		   cout<<"缺少運算符"<<endl;
		   break;
		case 4://缺少右括號,假設已經輸入右括號,轉到狀態11
		   state.push(11);
		   symbol.push(')');
		   cout<<"缺少右括號"<<endl;
		   break;
		case 5:
		   a++;
		   cout<<"*號無效,應該輸入+號!"<<endl;
		case 6:
		   a++;
	}
}

int main()
{
	int s;
	char *a;//當前操作符
	char how;
	int num;
	int b;
	char A;
	while(1)
	{
	   cin>>inputstream;
	   a=inputstream;
	   state.push(0);//0狀態入棧
	   
	   while(*a!='\0')
	   {
			b=0;num=0;how='\0';A='\0';
			s=state.top();
			action(s,a,how,num,A,b);
			if(how=='s')//移進
			{
				cout<<"移進"<<endl;
				symbol.push(*a);
				state.push(num);
				a++;
			}
			else if(how=='r')//規約
			{
				for(int i=0;i<b;i++)
				{
					if(!state.empty())
					state.pop();
					if(!symbol.empty())
					symbol.pop();
				}
			 int t=state.top();
			 symbol.push(A);
			 state.push(go(t,A));
			}
			else if(how=='a')//接受
				break;
			else
			{
				error(s,num,a);//錯誤處理    
			}
		}
	 cout<<"成功接受"<<endl;
	}
	return 0;
}

4、總結

學完語法分析這章後,對LL、LR、SLR、LR(1)、LALR還有着不少疑問。

LL是常用的自上而下的語法分析,方法比較簡單容易理解,從非終結符到終結符,樹節點到葉節點;LR文法則是自下而上,將終結符一步步規約縮減,從葉節點直到規約成樹根,LR(0)-SLR、LR(1)是對是否規約衝突選擇的不同方法,LALR瞭解較少,爲了處理LR(1)的同心集合的衝突設置的文法。

參考了部分教材和代碼後,簡單編寫(理解並改動部分)了詞法分析和語法分析器(也是老師作業- -)。

詞法分析器作用無非是剔除無關字符、將特殊字符分類放到表中作爲後續(如語法分析的操作符+/-/*等)、報錯(主要是鍵入錯誤)。

語法分析器顧名思義,就是判斷句子說法是否正確,例如:+號左右是否都是數字或未知數,遇到不合理的‘=+*’就要相應報錯;進一步的語法分析,除了語句還有聲明、函數名、變量聲明....


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