語法分析~LL1的實現


語法分析之 LL1分析法實現

一、設計目的

根據某一文法編制調試LL(1)分析程序,以便對任意輸入的符號串進行分析。本次實驗的目的主要是加深對預測分析LL(1)分析法的理解。

二、設計要求

程序輸入/輸出示例:

對下列文法,用LL(1)分析法對任意輸入的符號串進行分析:

原文法:

E->E+T|E-T|T

T->T*F|T/F|F

F->id|(E)|num

其中: id: a-f, A-F,num:0-9

消左遞歸:

E->TA A->+TA A->-TA A->e

T->FB B->*FB B->/FB B->e

F->i F->(E) F->n

其中:i:id, n:num, e:epsilonE->TG

FIRST集和FOLLOW集:

TA +TA -TA e FB *FB /FB e i (E) n
FIRST i,(,n + - e i,(,n * / e i ( n
E A T B F
FOLLOW $,) | $,) +,-,\(,) | +,-,\),) *,/,+,-,$,)

輸出的格式如下:

(1)輸入一以#結束的符號串(包括+—*/()i#):

(2)輸出過程如下:

輸入 輸出
\(E | (a-1)*(3+4/2)+((8*2))\) E->TA

(3)輸入符號串為非法符號串(或者為合法符號串)

注意:

1.表達式中允許使用運算符(+-*/)、分割符(括號)、字符i,結束符#;

2.如果遇到錯誤的表達式,應輸出錯誤提示信息(該信息越詳細越好);

3.測試用的表達式可以事先放在文本文件中,一行存放一個表達式,同時以分號分割。同時將預期的輸出結果寫在另一個文本文件中,以便和輸出進行對照;

三、設計說明

1. 需求分析:

  a****) 輸入及其范圍

​ 輸入為文法,表達式中允許使用運算符(+-*/)、分割符(括號)、字符a。

b****) 輸出形式

輸入 輸出
\(E | (a-1)*(3+4/2)+((8*2))\) E->TA

c) 程序功能

根據輸入的文法進行分析,利用LL(1)控制程序根據顯示棧棧頂內容、向前看符號以及LL(1)分析表,對輸入符號串自上而下的分析過程

d) 測試數據

​ 輸入:文件“fin.txt”輸入待分析串

輸出:命令行界面輸出預測分析表,LL(1)分析過程輸出至“fout.txt”

2. 概要設計

a****)數據類型的定義

​ vector<vector > table(5,vector (9)) //預測分析表

vector G //消除左遞歸后的文法產生式

map<char, int> index //文法符號到下標的轉換字典

string terminal("in+-*/()$") //終結符

string nonTerminal("EATBF") //非終結符

vector First // 產生式右部符號串的first集

vector Follow //非終結符的follow集

b****)主程序流程

![img](file:///C:/Users/CARRAW~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)

3. 詳細設計

\1. int main()

{

for(文法G每個產生式itG,itFirst為其右部符號串的first集){

​ x = itG左部非終結符號的下標;

​ for(itFirst中的每個終結符號first){

​ y = 終結符號first的下標;

​ 把itG加入分析表表G[x][y];

}

if(終結符號first == epsilon)

for(Follow集中的每個符號follow){

​ y = follow的下標;

​ 把itG加入分析表G[x][y];

}

​ }

​ for(所有非終結符號的Follow集)

if(對應表項為空)

寫入synch;

​ 將分析表輸出到命令行界面;

return analysis();

}

\2. int analysis(void)

{

從文件fin.txt讀取待分析串到s;

s末尾加‘$’;

​ 分析棧vector analyStack;

​ 向棧壓入‘$’、‘E’;

​ ip指向s的第一個字符;

​ do{

​ top是棧頂符號;

​ cur是ip所指向的輸入符號;

​ if(cur是字母) cur = ‘i’;

​ if(cur是數字) cur = ‘n’;

​ if(top是終結符號或‘$’){

​ if(top == cur){從棧頂彈出cur;ip前移一個位置;}

​ else error;

​ }

​ else{

​ x = top對應下標; y = cur對應下標;

​ 產生式production = table[x][y];

​ if(production非空){

​ 棧頂彈出cur;

​ 把production右部逆序壓棧;

​ 輸出production;

​ }

​ else error;

​ }

​ while(top != ‘$’);

}

四、運行結果及分析

1.測試數據

fin.txt文件入字符串:(a-1)(3+4/2)+((82))

2.測試輸出的結果

img)

輸出文件:

img

img

img

3.設計和思考

主要的難點在於對LL(1)的理解部分,消除二義性、消除左遞歸、提取左因子,判斷是否為LL(1)文法,然后開始整理思路進行編碼階段。開始要對錯誤的文法進行分析,並提示詳細的錯誤信息。思考之后實現了表達式中允許使用運算符(+-*/)、分割符(括號)、字符a。

五、總結

本次課程設計是本周實驗來難點最大的一次作業,首先需要溫習LL(1)的知識,如何消除左遞歸,區別二義性文法,以及對文法的分析。在實驗的過程中,最重要的還是要理順思路,想好解決辦法,這也是我經過不斷實驗總結出的自我思考的方法。然后就進入了編碼階段,此次編碼也有一定的難度,在代碼量以及代碼的整體設計上都有了提升,也是最值得思考的地方。最后,通過實驗報告的書寫,以及參考資料的查找,對今后的學習和工作都有很大的幫助。

代碼

#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>
#include <string>
#include <map>
#include <stdexcept>

using namespace std;

// 預測分析表
vector<vector<string> > table(5, vector<string>(9));
// 文法的產生式
vector<string> G = {"E->TA", "A->+TA", "A->-TA", "A->e", "T->FB", "B->*FB", "B->/FB", "B->e", "F->i", "F->(E)", "F->n"};

// 文法符號到下標轉換
map<char, int> index = {{'E', 0}, {'A', 1}, {'T', 2}, {'B', 3}, {'F', 4}, {'i', 0}, {'n', 1}, {'+', 2}, {'-', 3}, {'*', 4}, {'/', 5}, {'(', 6}, {')', 7}, {'$', 8}, {'e', 9}};
// 終結符
string terminal("in+-*/()$");
// 非終結符
string nonTerminal("EATBF");

// 產生式右部的first集
vector<string> First = {"i(n", "+", "-", "e", "i(n", "*", "/", "e", "i", "(", "n"};

// 非終結符的follow集
vector<string> Follow = {"$)", "$)", "+-$)", "+-$)", "*/+-$)"};


int analysis(void);


// 預測分析過程
int analysis(void) {
	ifstream fin("fin.txt");
	if (!fin.is_open()) {
		cout << "輸入文件不存在 fin.txt."  << endl;
		return 1;
	}
	ofstream fout("fout.txt");
	if (!fout.is_open()) {
		cout << "無法打開輸出文件 fout.txt." << endl;
		return 1;
	}

	//輸入緩沖區
	string s;
	fin >> s;
	cout << "成功讀取待分析串:" << endl << s << endl;
	int wid = s.length() + 1;
	s.push_back('$');

	//分析棧
	vector<char> analyStack;
	analyStack.push_back('$');
	analyStack.push_back('E');

	// 棧頂和當前輸入
	char top = '\0', cur = '\0';
	auto ip = s.begin();

	// 輸出頭
	fout << left << setw(wid + 10) << "棧" << right << setw(wid) << "輸入" << "    " << "輸出" << endl;
	do {
		// 輸出當前棧和當前剩余輸入
		string str1(analyStack.begin(), analyStack.end());
		string str2(ip, s.end());
		fout << left << setw(wid + 10) << str1 << right << setw(wid) << str2 << "    ";

		// 棧頂和當前輸入符號
		top = analyStack.back();
		cur = *ip;

		// 標識符及數字變換
		if (isalpha(cur))
			cur = 'i';
		else if (isdigit(cur))
			cur = 'n';

		// 棧頂是終結符號或$
		if (terminal.find(top) != terminal.npos || top == '$') {
			if (top == cur) {
				analyStack.pop_back();
				++ip;
				fout << endl;
			} else {
				fout << "出錯! 不匹配,彈出" << top << endl;
				analyStack.pop_back();
			}
		}
		// 棧頂非終結符
		else {
			//坐標轉換
			int x = index.at(top);
			int y;
			try {
				y = index.at(cur);
			} catch (out_of_range) {
				fout << "輸入字符非法!" << endl;
				break;
			}

			// 產生式
			string production = table.at(x).at(y);
			// 產生式非空
			if (!production.empty()) {
				if (production == "synch") {  //同步
					fout << "出錯!synch,彈出" << top << endl;
					analyStack.pop_back();
				} else { //正常分析
					analyStack.pop_back();
					string expr(production.begin() + 3, production.end());
					if (expr == "e")    //epsilon產生式
						expr = "";
					// 逆序壓棧
					for (auto iter = expr.rbegin(); iter != expr.rend(); ++iter)
						analyStack.push_back(*iter);
					// 輸出產生式
					fout << production << endl;
				}
			} else { //表項空白
				fout << "出錯!空白,跳過" << *ip << endl;
				++ip;
			}
		}
	} while (top != '$');
	cout << endl << "分析結果已輸出至 fout.txt." << endl;
	return 0;
}


int main() {
	//算法4.2 構造預測分析表
	// 遍歷G的每個產生式
	for (auto itG = G.begin(), itFirst = First.begin(); itG != G.end() && itFirst != First.end(); ++itG, ++itFirst) {
		// 非終結符下標轉換
		int x = index.at(*(itG->begin()));
		for (auto first = itFirst->begin(); first != itFirst->end(); ++first) {
			if (*first != 'e') {
				int y = index.at(*first);
				table.at(x).at(y) = *itG;
			} else {
				for (auto follow = Follow.at(x).begin(); follow != Follow.at(x).end(); ++follow) {
					int y = index.at(*follow);
					table.at(x).at(y) = *itG;
				}
			}
		}
	}
	// 寫入同步信息
	for (string::size_type i = 0; i < nonTerminal.length(); ++i) {
		int x = index.at(nonTerminal.at(i));
		for (vector<string>::size_type j = 0; j < Follow.at(i).length(); ++j) {
			int y = index.at(Follow.at(i).at(j));
			if (table.at(x).at(y).empty())
				table[x][y] = "synch";
		}
	}
	// 輸出預測分析表
	cout << "預測分析表:" << endl;
	// 輸出終結符
	for (string::size_type i = 0; i < terminal.size(); ++i)
		cout << '\t' << terminal[i];
	cout << endl;
	// 輸出非終結符
	for (string::size_type x = 0; x < nonTerminal.size(); ++x) {
		cout << nonTerminal[x];
		// 輸出產生式
		for (string::size_type y = 0; y < table.at(x).size(); ++y)
			cout << '\t' << table.at(x).at(y);
		cout << endl;
	}
	cout << endl;
	return analysis();
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM