[コンパイルの原則]実験2:再帰降下アナライザーを実装する

実験内容

高級言語の教科書3.2文法の再帰降下分析プログラムを実現します。
教科書3.2文法要件:
本で提供される入力文字列i1 *(i2 + i3)、または自分で定義した他の記号文字列を使用できます。スタックのすべての内容を出力し、分析結果を提供します。

開発環境

Windows 10
ビジュアルスタジオ2019

データ構造

1シンボルスタック

スタックは、アクセス制限のある線形テーブルであり、片端でのみ挿入または削除操作を許可します。「先入れ先出し」の原則に従います。
スタック図
再帰降下分析法はトップダウン分析法であり、文法の各非終端記号は再帰プロセス(関数)に対応しており、スタックを使用して分析プロセスを表すことができます。

2分析プロセス

シリアルナンバー シンボルスタック 分析する文字 表情
0 i *(i + i)#
1 #E i *(i + i)# E→TE '
2 #E'T i *(i + i)# T→FT '
#E'T'F i *(i + i)# F→i
4 #E'T ' i *(i + i)# T '→* FT'
5 #E'T'F i *(i + i)# F→(E)
6 #あなたも i *(i + i)# E→TE '
7 #E'T'E'T i *(i + i)# T→FT '
8 #E'T'E'T'F i *(i + i)# F→i
9 #E'T'E'T ' i *(i + i)# T '→ε
10 #あなたも' i *(i + i)# E '→+ TE'
11 #E'T'E'T i *(i + i)# T→FT '
12 #E'T'E'T'F i *(i + i)# F→i
13 #E'T'E'T ' i *(i + i)# T '→ε
14 #あなたも' i *(i + i)# E '→ε
15 #E'T ' i *(i + i)# T '→ε
16 #E ' i *(i + i)# E '→ε
17

 i *(i + i)分析スタック図

3アレイ

1.分析文字列配列char token [token_size]:分析する文字列を格納します。
2.シンボルスタックchar stack [10] = {'#'}:シンボルスタックの内容を保存し、最初に '#'を押します。

実験ステップ

分析と設計

教科書のサンプルの質問はすでに分析的に詳細になっ
ています。1.左再帰が排除されました。
2.バックトラックなし。
再帰的降下分析器の構築に直接進みます。
再帰降下分析法の大まかなフローチャート

プログラミング

1グローバル変数

①token_len:分析する文字列の長さを格納するint型変数。
②step:現在の分析ステップを記録するint型変数。

2スタック

①char stack [10] = {'#'}:シンボルスタックの内容を格納するcharタイプの配列。最初は '#'を押します。
②stack_top:int型変数。シンボルスタックのトップポインター。

3アレイ

token [token_size]:分析する文字列を格納するchaタイプの配列。

4つの機能

①非終端記号式:
void E();
void E1();
void T();
void T1();
void F();
②印刷関数:void print()。

5疑似コード

「コンパイル原理チュートリアル(第4版)」P52-53をご覧ください

void match(token t){
	if (lookahead == t)
			lookahead = nexttoken;
	else
			error();
}

void E(){
	T();
	E();
}

void E'(){
	if (lookahead == '+'){
		match('+');
		T();
		E'();
	}
}

void T(){
	F();
	T'();
}

void T'(){
	if (lookahead == '*'){
		match('*');
		F();
		T'();
	}
}

void F(){
	if (lookahead == 'i')
		match('i');
	else if (lookahead == '('){
		match('(');
		E();
		if (lookahead == ')')
			match(')');
		else
			error();
	}
	else
		error();
}

実行してデバッグする

デバッグ状況に応じて、対応するコードを変更します。

演算結果

i *(i + i)は有効な文字列です
++は不正な文字列です

発生した問題と解決策

1 E 'およびT'スタッキング

E 'は文字に属していないため、もともとはストレージを考慮して文字列配列を実装するように計画されていました。多くの問題があるため、E'と '\' 'の2つの文字に従ってE'を直接格納し、T 'は同じです根拠。

2スタックの実装

経験

再帰降下アナライザーの構造をより深く理解し、スタックの実現に熟練している。

実験コード

1つのメイン関数main()

int main(void){
    
    
	printf("请输入字符串(以#结束):");		//输入待分析字符串
	while (token[lookahead] != '#'){
    
    		//如果没有按下#结束
		char scan_char;						//扫描字符
		do{
    
    									//若没有扫描到结束符#,就继续扫描
			scanf_s("%c", &scan_char,1);
			token[token_len] = scan_char;	//将扫描到的字符保存到待分析串数组中
			token_len++;
		} while (scan_char != '#');
		printf("\n");
		printf("序号\t");
		printf("符号栈\t\t");
		printf("待分析字符\t");
		printf("表达式\n");
		print();							//打印当前步骤信息
		printf("\n");						//换行
		stack[++stack_top] = 'E';			//将E()压入栈中,开始分析
		step++;								//分析步数+1
		E();
		if (token[lookahead] == '#')		//若待分析字符为#,说明符合文法
			printf("\n分析成功,合法字符串!");
		else
			printf("\n分析失败,非法字符串!");
	}
	return 0;
}

2 E()

/*****************************************************************************
 *函数名称:E()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void E(){
    
    
	print();					//打印当前步骤信息
	printf("E->TE'\n");			//输出表达式
	stack[stack_top] = 'E';		//将产生式右→左压栈
	stack[++stack_top] = '\'';	//代表E'
	stack[++stack_top] = 'T';
	step++;						//分析步数+1
	T();
	E1();
}

3 E1()

/*****************************************************************************
 *函数名称:E1()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void E1(){
    
    
	if (token[lookahead] == '+'){
    
    		//若待分析字符匹配‘+’
		print();						//打印当前步骤信息
		printf("E'->+TE'\n");			//输出表达式
		stack[--stack_top] = 'E';		//将产生式右→左压栈
		stack[++stack_top] = '\'';
		stack[++stack_top] = 'T';
		step++;							//分析步数+1
		lookahead++;					//因为匹配到一个终结符,所以分析下一个字符
		T();
		E1();
	}
	else{
    
    
		print();						//打印当前步骤信息
		printf("E'->ε\n");				//输出表达式
		stack[stack_top--] = NULL;		//出栈
		stack[stack_top--] = NULL;		//E'虽然为一个非终结符,但占两个字符,T'同
		step++;							//分析步数+1
	}
}

4 T()

/*****************************************************************************
 *函数名称:T()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void T(){
    
    
	print();					//打印当前步骤信息
	printf("T->FT'\n");			//输出表达式
	stack[stack_top] = 'T';		//将产生式右→左压栈
	stack[++stack_top] = '\'';
	stack[++stack_top] = 'F';
	step++;						//分析步数+1
	F();
	T1();
}

5 T1()

/*****************************************************************************
 *函数名称:T1()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void T1(){
    
    
	if (token[lookahead] == '*'){
    
    		//若待分析字符匹配‘*’
		print();						//打印当前步骤信息
		printf("T'->*FT'\n");			//输出表达式
		stack[--stack_top] = 'T';		//将产生式右→左压栈
		stack[++stack_top] = '\'';
		stack[++stack_top] = 'F';
		step++;							//分析步数+1
		lookahead++;					//因为匹配到一个终结符,所以分析下一个字符
		F();
		T1();
	}
	else{
    
    
		print();						//打印当前步骤信息
		printf("T'->ε\n");				//输出表达式
		stack[stack_top--] = NULL;		//出栈
		stack[stack_top--] = NULL;
		step++;							//分析步数+1
	}
}

6 F()

/*****************************************************************************
 *函数名称:F()
 *函数类型:void
 *参数:void
 *功能:分析文法,输出信息
 *****************************************************************************/
void F(){
    
    
	if (token[lookahead] == 'i'){
    
    		//若待分析字符匹配‘i’
		print();						//打印当前步骤信息
		printf("F->i\n");				//输出表达式
		stack[stack_top--] = NULL;		//出栈
		step++;							//分析步数+1
		lookahead++;					//因为匹配到一个终结符,所以分析下一个字符
	}
	else if (token[lookahead] == '('){
    
    	//若待分析字符匹配‘(’
		print();						//打印当前步骤信息
		printf("F->(E)\n");				//输出表达式
		stack[stack_top] = 'E';			//将产生式右→左压栈
		step++;							//分析步数+1
		lookahead++;					//因为匹配到一个终结符,所以分析下一个字符
		E();
		if (token[lookahead] == ')'){
    
    	//若待分析字符匹配‘)’
			lookahead++;				//因为匹配到一个终结符,所以分析下一个字符
		}
		else{
    
    
			printf("没有')'匹配!\n");
			return;
		}
	}
	else{
    
    
		printf("error\n");
		return;
	}
}

7 print()

/*****************************************************************************
 *函数名称:print()
 *函数类型:void
 *参数:void
 *功能:打印信息
 *****************************************************************************/
void print(){
    
    
	int i;
	printf("%d\t", step);						//打印分析第step步
	for (i = 0; i <= stack_top; i++)			//输出分析栈中内容
		printf("%c", stack[i]);
	if (stack_top < 7)							//每列对齐
		printf("\t\t");
	else
		printf("\t");
	for (i = 0; i < lookahead; i++){
    
    			//若字符已被分析,它的位置置空,保持列对齐
		token[i] = ' ';
		printf("%c", token[i]);
	}
	for (i = lookahead; i < token_len; i++)		//输出剩余待分析字符
		printf("%c", token[i]);
	printf("\t");								//为输出表达式做准备
}

おすすめ

転載: blog.csdn.net/qq_44714521/article/details/106972173