【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)

这篇具有很好参考价值的文章主要介绍了【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

由于代码较长,csdn对文章总长度有字数限制,想只看完整代码的请移步另一篇博客。

https://blog.csdn.net/qq_46640863/article/details/125705891
理论与习题讲解移步视频
https://www.bilibili.com/video/BV1zu4y1C7SL

一、实验内容

1.实现LL(1)分析算法
2.输入:教材中的算术表达式文法;待分析的语句(如i+i*i)
3.输出:语句的分析过程(参见ppt例题)
4.要求:LL(1)分析表程序自动生成。如果使用已知的分析表,实验分数会降低。

二、实验目的

通过设计、编制、调试一个具体的文法分析程序,深入理解LL(1)预测分析法的基本分析原理,理解FIRST集、FOLLOW集的构造方法并对其加以实现,构造LL(1)预测分析表并利用分析表对语句、文法进行分析。

三、实验分析

书中算术表达式文法示例如下:
G [ E ] : ( 1.1 ) E → E + T ∣ T T → T ∗ F ∣ F F → ( E ) ∣ i G[E]:(1.1)\\ E\to E+T|T\\ T\to T*F|F\\ F\to(E)|i G[E]:(1.1)EE+TTTTFFF(E)i
这是一个算术表达式文法,满足*优先于+,且*和+都服从左结合。这个文法不存在二义性,因为运算符优先级被分为了三个层次:“+”为一层,“*”为一层,括号“()”与运算对象“ i i i”为另一层,这三层优先级依次递增。用非终结符 E E E表示文法的开始符号,再增加 T T T F F F表示不同的层次。即:离开始符越近的产生式优先级越低,离开始符越远的产生式优先级越高。
使用一个消除了二义性的文法可以进行自顶向下的语法分析如递归下降分析与LL(1)分析法。但是,还需要先消除左递归与回溯。
设关于非终结符 A A A的直接左递归产生式为 A → A α ∣ β A\to A\alpha |\beta AAαβ,对其消除左递归,可以将其改写为:
{ A → β A ′ A ′ → α A ′ ∣ ϵ \begin{cases} A\to \beta A'\\ A'\to \alpha A'|\epsilon \end{cases} {AβAAαAϵ
其中, A ′ A' A为新引入的非终结符, ϵ \epsilon ϵ为空字不可缺少。
对文法 G [ E ] ( 1.1 ) G[E](1.1) G[E](1.1)消除左递归可以得到文法 G [ E ] ( 1.2 ) G[E](1.2) G[E](1.2)
G [ E ] ( 1.2 ) E → T E ′ E ′ → + T E ′ ∣ ϵ T → F T ′ T ′ → ∗ F T ′ ∣ ϵ F → ( E ) ∣ i G[E](1.2)\\ E\to TE'\\ E'\to+TE'|\epsilon\\ T\to FT'\\ T'\to *FT'|\epsilon\\ F\to(E)|i G[E](1.2)ETEE+TEϵTFTTFTϵF(E)i
回溯发生的原因在于候选式存在共同的左公因子。如关于 A A A产生式为 A → δ β 1 ∣ δ β 2 ∣ . . . ∣ δ β i ∣ β i + 1 ∣ . . . ∣ β j A\to \delta\beta_1|\delta\beta_2|...|\delta\beta_i|\beta_{i+1}|...|\beta_j Aδβ1δβ2∣...∣δβiβi+1∣...∣βj,则可以将其改写为:
{ A → δ A ′ ∣ β i + 1 ∣ . . . ∣ β j A ′ → β 1 ∣ . . . ∣ β i \begin{cases} A\to\delta A'|\beta_{i+1}|...|\beta_j\\ A'\to \beta_1|...|\beta_i \end{cases} {AδAβi+1∣...∣βjAβ1∣...∣βi
由于算术表达式文法不存在左公因子,所以无需消除回溯。
在程序实现时,可以消除文法中的或符号,即对其进行处理:
G [ E ] ( 1.3 ) E → T E ′ E ′ → + T E ′ E ′ → ϵ T → F T ′ T ′ → ∗ F T ′ T ′ → ϵ F → ( E ) F → i G[E](1.3)\\ E\to TE'\\ E'\to+TE'\\ E'\to\epsilon\\ T\to FT'\\ T'\to *FT'\\ T'\to\epsilon\\ F\to(E)\\ F\to i G[E](1.3)ETEE+TEEϵTFTTFTTϵF(E)Fi
为了LL(1)构造分析表 M M M,需要构造与文法相关的集合FIRST与FOLLOW。
FIRST( α \alpha α)表示 α \alpha α的所有可能推导的开头终结符或可能的空字 ϵ \epsilon ϵ。FIRST构造方法为反复运用下述规则,直到每个集合的FIRST集合不再变化为止。
1.若存在产生式 X → a X\to a Xa,且 a a a为终结符,则把 a a a加入FIRST( X X X)中。若存在 X → ϵ X\to\epsilon Xϵ,则 ϵ \epsilon ϵ也加到FIRST( X X X)中。
2.若存在 X → Y . . . X\to Y... XY...,且 Y Y Y为非终结符,则将FIRST( Y Y Y)中所有非 ϵ \epsilon ϵ元素加入到FIRST( X X X)中。若 X → Y 1 Y 2 . . . Y k X\to Y_1Y_2...Y_k XY1Y2...Yk Y 1 Y_1 Y1~ Y i Y_i Yi都是非终结符且产生式中存在 ϵ \epsilon ϵ,则将FIRST( Y j Y_j Yj) ( j = 1 , 2 , . . . , i ) (j=1,2,...,i) (j=1,2,...,i)的所有非 ϵ \epsilon ϵ元素加入到FIRST( X X X)中,当 Y 1 Y_1 Y1~ Y k Y_k Yk都含有 ϵ \epsilon ϵ产生式时,把 ϵ \epsilon ϵ加入到FIRST( X X X)中。
对本实验中涉及的算术表达式文法构造FIRST集合。FIRST( E ′ E' E)= { + , ϵ } \{+,\epsilon\} {+,ϵ},FIRST( T ′ T' T)= { ∗ , ϵ } \{*,\epsilon\} {,ϵ},FIRST( F F F)= { ( , i } \{(,i\} {(,i}。由 T → F . . . T\to F... TF... E → T . . . E\to T... ET...,可得FIRST( T T T)=FIRST( E E E)=FIRST( F F F)= { ( , i } \{(,i\} {(,i}
对每个非终结符构造FOLLOW集合,需要连续使用以下规则:
1.对文法开始符号 S S S,将 # \# #加入FOLLOW( S S S)。
2.若存在产生式 A → . . . B β A\to ...B\beta A...,将FIRST( β \beta β)中除 ϵ \epsilon ϵ的元素加入FOLLOW( B B B)。
3.若存在产生式 A → . . . B A\to ...B A...B A → . . . B β A\to ...B\beta A... ϵ ∈ \epsilon\in ϵFIRST( β \beta β),则把所有元素加到FOLLOW( B B B)。
构造FOLLOW集合时,按规则1,FOLLOW( E E E)= { # } \{ \#\} {#}。按规则2,由 E → T E ′ E\to TE' ETE,FOLLOW( T T T)= { + } \{+\} {+}。由 T → F T ′ T\to FT' TFT,FOLLOW( F F F)= { ∗ } \{*\} {},由 F → ( E ) F\to (E) F(E),FOLLOW( E E E)= { ) , # } \{),\# \} {),#}。按规则3,由 E → T E ′ E\to TE' ETE,FOLLOW( E ′ E' E)= { ) , # } \{),\#\} {),#}。由 E → T E ′ E\to TE' ETE E → ϵ E\to\epsilon Eϵ,FOLLOW( T T T)= { + , ) , # } \{+,),\#\} {+,),#}。由 T → F T ′ T\to FT' TFT,FOLLOW( T ′ T' T)= { + , ) , # } \{+,),\# \} {+,),#}。由 T → F T ′ T\to FT' TFT T ′ → ϵ T'\to \epsilon Tϵ,FOLLOW( F F F)= { ∗ , + , ) , # } \{*,+,),\# \} {,+,),#}
在构造完FIRST集合与FOLLOW集合后,可以构造分析表 M M M。对于文法中的每一个产生式,对每个终结符 a ∈ a\in aFIRST( A A A),把 A → α A\to \alpha Aα加入 M [ A , a ] M[A,a] M[A,a]中,其中 a ∈ a\in aFIRST( α \alpha α)。若 ϵ ∈ \epsilon\in ϵFIRST( A A A),则对任何属于FOLLOW( A A A)的终结符 b b b,将 A → ϵ A\to\epsilon Aϵ加入到 M [ A , b ] M[A,b] M[A,b]。将其他表项都认为是出错。算术表达式文法的分析表如下:

i i i + + + ∗ * ( ( ( ) ) ) # \# #
E E E E → T E ′ E\to TE' ETE E → T E ′ E\to TE' ETE
E ′ E' E E ′ → + T E ′ E'\to +TE' E+TE E ′ → ϵ E'\to\epsilon Eϵ E ′ → ϵ E'\to\epsilon Eϵ
T T T T → F T ′ T\to FT' TFT T → F T ′ T\to FT' TFT
T ′ T' T T ′ → ϵ T'\to\epsilon Tϵ T ′ → ∗ F T ′ T'\to *FT' TFT T ′ → ϵ T'\to\epsilon Tϵ T ′ → ϵ T'\to\epsilon Tϵ
F F F F → i F\to i Fi F → ( E ) F\to (E) F(E)

输入串以 # \# #作为结束标志,分析栈栈底先放入 # \# #。分析程序根据分析栈栈顶符号 x x x和当前输入符号 a a a决定分析器动作。若 x = a = # x=a=\# x=a=#,分析成功。若 x = a ≠ # x=a\ne \# x=a=#,栈顶符号与输入符号匹配,则 x x x出栈,继续对下一个字符进行分析。若栈顶为非终结符 A A A,则查表。若为一个 A A A的产生式,则将 A A A出栈,将产生式右部符号逆序压入栈。若产生式为 A → ϵ A\to\epsilon Aϵ,则只将 A A A出栈。若表中该表项为空,则出错。

四、实验流程

4.1 整体流程

首先从文件中读入文法,识别出文法的终结符与非终结符,对文法进行消除左递归、消除回溯、消除或。再次识别出文法的终结符与非终结符,对每个符号与短语求出其FIRST集合与FOLLOW集合,自动分析生成建立分析表。输入待分析的文法,对其进行分析。整体流程如下图所示。
【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)

4.2 求FIRST

对字符或短语求FIRST集合时,往往需要用到递归。若在递归时发现 c c c字符的FIRST集合已经求解完毕,则直接返回。若当前字符 c c c是终结符,则FIRST集合为自己,返回。若不是终结符,则对其产生式进行遍历。若产生式中就是空字,则将空字加入FIRST集合。否则,遍历产生式的字符。递归的求解当前字符的FIRST集合,将该字符FIRST集合除了空字以外的元素加入。若该字符的FIRST集合存在空字,则遍历下一字符,如此循环。求解短语的FIRST集合的过程与求解某一非终结符的产生式的FIRST集合的流程是类似的。求FIRST集合的流程如下图。
【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)

4.3 求FOLLOW

对字符或短语求FOLLOW集合时,也需要用到递归。对于文法开始符号,先将#加入其FOLLOW集合。对于每个非终结符,判断每条产生式,若存在 A → . . . B b A\to ...Bb A...Bb,则将 b b b B B B入的FOLLOW集合。对每个产生式从右往左遍历,对于当前标识符之后的字符,若是非终结符,则将这个非终结符的FIRST集加入当前字符的FOLLOW集,这一过程需要递归地进行。若 A → B β A\to B\beta A ϵ ∈ \epsilon\in ϵFIRST( β \beta β),将FOLLOW( A A A)加入FOLLOW( B B B)。每次都要反复进行此过程。
【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)

4.4 求分析表并分析输入串

构造分析表的流程与上文是一致的:若 a a a属于FIRST( A A A),把 A → a A\to a Aa加入到 M [ A , a ] M[A,a] M[A,a]中。若 ϵ \epsilon ϵ属于FIRST( A A A),则对FOLLOW( A A A)的终结符 b b b加入 A → ϵ A\to\epsilon Aϵ。分析输入串的流程与上文一致,在此也不赘述。

五、实验代码

5.1 数据结构

利用S表示文法的开始符,利用VNVT分别表示非终结符与终结符。
字符的FIRST集合用map存储。map的键为字符,值为FIRST集合,用set存储。字符的FIRST集表被定义为:map<char, set<char>> firstSet
短语的FIRST集合也用map存储。map的键为字符串,即短语。值为FIRST集合,用set存储。其被定义为:map<string, set<char>> firstSetS
字符的FOLLOW集合用map存储。map的键为字符,值为FOLLOW集合,用set存储。字符的FOLLOW集表被定义为:map<char, set<char>> followSet;
利用MAP表示产生式表,利用map存储。map的键为产生式的左部,值为产生式右部。因为一个非终结符可能有多个右部,所以值用vector<string>存储,可以存储多个右部字符串。map<char, vector<string>> expressionSet;
预测分析表本体利用二维数组存储。vector<vector<string>> table;
文件读取到的文法、消左递归的文法、消除回溯的文法、增广的文法,可以用于求解FIRST与FOLLOW的文法存储在不同的变量名中。

vector<string> filebuffer;//文件读取的缓冲区
vector<string> expression_withoutrecursion;//消左递归
vector<string> expression_withoutback;//消完左递归和回溯
vector<string> inputExpression;//最后增广完成,用于进行分析的文法

5.2 部分核心算法设计

5.2.1 消除左递归与回溯并消除或

需要着重介绍的就是此部分的代码。在读取到的文件中,用字符”->”代替箭头。对文法消除左递归时,判断文法某一个产生式的第0项与第3项即”->”后的字符是否相同。若相同,则发现左递归。利用下式对 A → A α ∣ β A\to A\alpha|\beta AAαβ进行改写。为了方便程序的编写,所有符号都只用一个字符表示。从大写字母表中找一个没有被使用过的字母作为非终结符表示下式中的 A ′ A' A,用字母 e e e代替 ϵ \epsilon ϵ,表示空字。每次利用find()函数找到”|”,利用substr()截取子字符串,找到产生式中的 α \alpha α短语与 β \beta β短语。
{ A → β A ′ A ′ → α A ′ ∣ ϵ \begin{cases} A\to\beta A'\\ A'\to\alpha A'|\epsilon \end{cases} {AβAAαAϵ
在消除回溯时,判断第3个字符与每处”|”后的字符是否相同。若相同,则此产生式存在回溯,存在共同的左公因子。用同样的方法,找到每处”|”,用下式中的文法替换原有文法。同样的,用没有使用过的字符代替 A ′ A' A
{ A → δ A ′ ∣ β i + 1 ∣ . . . ∣ β j A ′ → β 1 ∣ . . . ∣ β i \begin{cases} A\to\delta A'|\beta_{i+1}|...|\beta_j\\ A'\to \beta_1|...|\beta_i \end{cases} {AδAβi+1∣...∣βjAβ1∣...∣βi
消除“或”时,同样需要找到每处”|”,并对含有”|”符号的字符串进行重新拼接处理。

void leftRecursion(){
	for (string str : filebuffer){
		if (str[0] == str[str.find('>') + 1]){
			char newch = 'A';
			for (newch = 'A'; newch <= 'Z'; newch++)
				if (VN.count(newch) == 0)
					break;
			VN.insert(newch);
			string newstr = "";
			string alpha = str.substr(4, str.find('|') - 4);
			//	cout << "\nalpha" << alpha << endl;
			//A->betaA'
			newstr = newstr + str[0] + (string)"->" + str.substr(str.find('|') + 1) + newch;
			expression_withoutrecursion.push_back(newstr);
			newstr = "";
			//A'->alphaA'|e
			newstr = newstr + newch + "->" + alpha + newch + "|" + 'e';
			//e表示空字符episilon
			VT.insert('e');
			expression_withoutrecursion.push_back(newstr);
		}
		else
			expression_withoutrecursion.push_back(str);
	}
	cout << "\n消除左递归:\n";
	for (string str : expression_withoutrecursion)
		cout << str << endl;
}
//消除回溯
void leftTraceback(){
	for (string str : expression_withoutrecursion){
		//发现回溯
		if (str[3] == str[str.find("|") + 1]){
			char newch = 'A';
			for (newch = 'A'; newch <= 'Z'; newch++)
				if (VN.count(newch) == 0)
					break;
			VN.insert(newch);
			string delta = "" + str[3];
			string newstr = "";
			//A->alphaA'
			newstr += str[0] + "->" + delta + newch;
			expression_withoutback.push_back(newstr);
			newstr = "";
			//A'->beta1|beta2
			string beta1 = str.substr(4, str.find('|') - 4);
			string beta2 = str.substr(str.find('|') + 1);
			newstr += newch + "->" + beta1 + '|' + beta2;
			expression_withoutback.push_back(newstr);
		}
		else
			expression_withoutback.push_back(str);
	}
	cout << "\n消除回溯:\n";
	for (string str : expression_withoutback)
		cout << str << endl;
}
//增广,删除竖线|
void deleteor(){
	for (string str : expression_withoutback){
		if (str.find('|') != string::npos){
			//将A->beta1|beta2拆成A->beta1和A->beta2
			string right1 = str.substr(3, str.find('|') - 3);
			//	cout << "right1" << right1 << endl;
			string newstr1 = "";
			newstr1 = str[0] + (string)"->" + right1;
			//	cout << "newstr1" << newstr1 << endl;
			inputExpression.push_back(newstr1);
			string right2 = str.substr(str.find('|') + 1);
			//	cout << "right2" << right2 << endl;
			string newstr2 = "";
			newstr2 += str[0] + (string)"->" + right2;
			//	cout << "newstr2" << newstr2 << endl;
			inputExpression.push_back(newstr2);
		}
		else
			inputExpression.push_back(str);
	}
	cout << "\n增广\n";
	for (string str : inputExpression)
		cout << str << endl;
}

5.2.2 求解FIRST

求字符与短语的FIRST集合的过程与4.2章节中的流程图是一致的。这是一个递归的过程。因为涉及到了求单个字符与短语,所以需要对函数进行重载。

//字符的求first集
void getFirst(char c){
	//已经求完了当前字符的first集
	if (firstSet.count(c))
		return;
	set<char> newset;
	//若c为终结符,则first为自己,结束
	if (VT.count(c)){
		newset.insert(c);
		firstSet[c] = newset;
		return;
	}
	for (string s : expressionSet[c]){
		//若存在X->e,将e加入
		if ('e' == c)
			newset.insert('e');
		else{
			for (char cur : s){
				if (!firstSet.count(cur))
					getFirst(cur);
				set<char> curFirst = firstSet[cur];
				for (auto curf : curFirst)
					newset.insert(curf);
				//将Y的first集合中的元素中不是e的加入
				if (!curFirst.count('e'))
					break;
				//如果加到了e,则循环回去,即X->Y1Y2..Yk时,Y1产生e,就需要加入Y2的first集合
			}
		}
	}
	firstSet[c] = newset;
}
//多个字符(字符串、短语)first集求法
void getFirst(string s){
	if (firstSetS.count(s))
		return;
	set<char> newset;
	int i = 0;
	while (i < s.length()){
		char cur = s[i];
		if (!firstSet.count(cur))
			getFirst(cur);
		//将当前字符的first集作为短语的first集
		set<char> rightSet = firstSet[cur];
		for (auto rs : rightSet)
			newset.insert(rs);
	//类似的,如果加到了e,则循环回去,即X->Y1Y2..Yk时,Y1产生e,就需要加入Y2的first集合
		if (rightSet.count('e'))
			i++;
		else
			break;
		//遍历完了当前语句后,将空字加入当前语句first集
		if (i == s.length())
			newset.insert('e');
	}
	firstSetS[s] = newset;
}

5.2.3 求解FOLLOW

求解FOLLOW集合的流程是迭代的。在调用初始化函数时,需要针对每一个字符反复的对其求解FOLLOW集合。

//求每个非终结符的follow
	for (char c : VN)
		getFollow(c);

求解某一字符FOLLOW集合的流程与章节4.3的流程图是一致的。

//某个字符的follow集合
void getFollow(char c){
	vector<string> list = expressionSet[c];
	set<char> leftFollowSet;
	if (followSet.count(c))
		leftFollowSet = followSet[c];
	//对文法开始符号,将#加入follow集合
	if (c == S)
		leftFollowSet.insert('#');
	for (char ch : VN) {
		for (string s : expressionSet[ch]) {
			for (int i = 0; i < s.length(); i++) {
				//若有A->aBb(a可为空),则将b加入B的follow集合
				if (c == s[i] && i + 1 < s.length() && VT.count(s[i + 1])) 
					leftFollowSet.insert(s[i + 1]);
			}
		}
	}
	followSet[c] = leftFollowSet;
	for (string s : list){
		int i = s.length() - 1;
		//对每个产生式从右往左遍历
		while (i >= 0)
		 {
			char cur = s[i];
			//对于当前标识符B
	//对于当前标识符之后的字符,若是非终结符,则将这个非终结符的first集加入当前字符的follow集
			if (VN.count(cur)){
				string right = s.substr(i + 1);
				set<char> rightFirstSet;
				if (!followSet.count(cur)) 
					getFollow(cur);
				set<char> curFollowSet = followSet[cur];
				//若A->aB或A->aB\beta且\beta->e
				//则把follow(A)加到follow(B)
				if (right.length() == 0)
					for (auto lfs : leftFollowSet) 
						curFollowSet.insert(lfs);
				else
					//将若A->Bbeta,将B后的字符的first(除e)加入
					if (right.length() == 1)
						if (!firstSet.count(right[0])) 
							getFirst(right[0]);
						rightFirstSet = firstSet[right[0]];
					else
						if (!firstSetS.count(right))
							getFirst(right);
						rightFirstSet = firstSetS[right];
					for (char var : rightFirstSet)
						if (var != 'e')
							curFollowSet.insert(var);
					//若A->Bbeta且e属于first(beta),将follow(A)加入follow(B)
					if (rightFirstSet.count('e'))
						for (auto lfs : leftFollowSet)
							curFollowSet.insert(lfs);
				}
				followSet[cur] = curFollowSet;
			}
			i--;
		 }
	}

}

5.2.4 文法分析

分析的过程与第三章节是一致的。首先,放 # \# #入分析栈,再放文法开始符 S S S入分析栈。当栈顶元素不是 # \# #时,如果栈顶元素与输入字符是一致的,则将栈顶弹出,扫描指针向后移动。若栈顶为其他终结符,则出错。若分析表中当前表项为空,则出错。若当前表项为空字,则出栈。否则,从分析表中找到当前非终结符 X X X表项 M [ X , a ] M[X,a] M[X,a],倒序将当前表项中产生式的右部入栈。其他情况均为出错情况。

void analyzeLL(){
	cout << "\nLL分析过程\n";
	cout << setw(20) << "stack" << setw(10) << "input" << setw(20) << "expression";
	cout << endl;
	analyzeStack.push('#');
	//#入栈
	analyzeStack.push(S);
	//开始符入栈
	displayLL();
	char X = analyzeStack.top();
	while (X != '#'){
		char a = strInput[index];
		if (X == a){
			action = "";
			analyzeStack.pop();
			index++;
		}
		else if (VT.count(X))
			return;//找到终结符,出错
		else if (find(X, a) == "")
			return;
		else if (find(X, a) == "e"){
			analyzeStack.pop();
			action = "";
			action += X;
			action += "->e";
		}//空字,出栈
		else{//其余情况,需要按分析表对栈顶元素进行推导
			string str = find(X, a);
			if (str != ""){
				action = "";
				action += X;
				action += "->";
				action += str;
				analyzeStack.pop();
				int len = str.length();
				for (int i = len - 1; i >= 0; i--)
				analyzeStack.push(str[i]);//栈顶元素出栈,按分析表和下一字符将分析表中元素入栈
			}
			else
			{
				cout << "\nERROR!\n";//出错,直接返回
				return;
			}
		}
		X = analyzeStack.top();
		displayLL();
	}
}

5.3 完整程序

https://blog.csdn.net/qq_46640863/article/details/125705891

六、运行结果

从文件中读取输入文法为算术表达式文法如下,这一文法存在左递归,且没有增广。【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)
因此,需要识别终结符与非终结符,对其进行消除左递归、消除回溯、增广处理,运行结果如下。为了简化程序,用A代替 E ′ E' E,用B代替 T ′ T' T,用e代替 ϵ \epsilon ϵ,其结果与第三章节推导的结果是完全一致的。
【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)
对于消除了”|”完成的文法,再次识别终结符与非终结符,并对每个非终结符输出其产生式右部。
【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)
在预处理工作完成后,即可对每个终结符与非终结符求解FIRST集合,求解非终结符的FOLLOW集合。其结果与第三章节的结果是完全一致的。
【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)
求解FIRST集合和FOLLOW集合完成后,构建分析表,其结果与第三章节的分析是一致的。
【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)
输入表达式 i ∗ i + i i*i+i ii+i,自动给出分析过程。
首先,将 # \# #与开始符 E E E压栈;根据 M [ E , i ] = E → T A M[E,i]=E\to TA M[E,i]=ETA,将 E E E出栈,将 T A TA TA逆序后入栈;根据 M [ T , i ] = T → F B M[T,i]=T\to FB M[T,i]=TFB,将 T T T出栈,将 F B FB FB逆序后入栈;根据 M [ F , i ] = F → i M[F,i]=F\to i M[F,i]=Fi,将 F F F出栈,将 i i i入栈;此时,栈顶与输入串匹配,弹出;此时栈顶为 B B B,根据 M [ B , + ] = B → ϵ M[B,+]=B\to\epsilon M[B,+]=Bϵ,空字符不入栈;此时栈顶为 A A A,根据 M [ A , + ] = A → + T A M[A,+]=A\to +TA M[A,+]=A+TA,将 + T A +TA +TA逆序后入栈。此时栈顶的+与输入串的+匹配,出栈。之后的分析步骤与流程与前文类似,在此不再赘述。
【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)
在对正确地文法分析完毕后,分析过程正确地停止,程序退出。文章来源地址https://www.toymoban.com/news/detail-456395.html

七、实验感悟

到了这里,关于【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【编译原理】LR(1)分析法:C/C++实现

    🌈 个人主页: Sarapines Programmer 🔥 系列专栏: 《编译原理奇遇记》 🔖墨香寄清辞:空谷幽篁风动,梦中仙鹤月明。 辗转千秋豪情在,乘风翱翔志不移。 目录结构 1. 编译原理之LR(1)分析法概念 1.1 编译原理 1.2 LR(1)分析法 2. 逆波兰式的产生及计算 2.1 实验目的 2.2 实验要求

    2024年02月03日
    浏览(35)
  • 编译原理之LL(1)语法分析实验(附完整C/C++代码与测试)

    先从键盘读入要分析的文法,由程序自动构造FIRST、FOLLOW 集以及SELECT集合,判断是否为LL (1)文法。 分析文法为G[E]: (0)E→ TE’   (1)E’→ +TE’ (2)E’→ε    (3)T→ FT’ (4)T’→ *FT’   (5)T’→ε     (6)F→ (E)    (7)F→a 若符合LL (1)文法,由程序自动构

    2024年02月04日
    浏览(41)
  • 层次分析法原理及实例(AHP)

    层次分析法(AHP) 层次分析法(analytic hierarchy process) ,简称AHP,是指将与决策总是有关的元素分解成目标、准则、方案等层次,在此基础之上进行定性和定量分析的决策方法。该方法是美国运筹学家匹茨堡大学教授萨蒂于20世纪70年代初,在为美国国防部研究\\\"根据各个工业

    2024年02月08日
    浏览(40)
  • 实验一 基于MATLAB语言的线性离散系统的Z变换分析法

    实验一 基于MATLAB语言的线性离散系统的Z变换分析法 一、实验目的 1. 学习并掌握 Matlab 语言离散时间系统模型建立方法; 2 .学习离散传递函数的留数分析与编程实现的方法; 3 .学习并掌握脉冲和阶跃响应的编程方法; 4 .理解与分析离散传递函数不同极点的时间响应特点

    2024年02月08日
    浏览(39)
  • 【AHP层次分析法】原理+应用步骤+旅游目的地选择实例应用

    实现标准之间相对重要程度,并给出决策方案中每个标准的权重,利用权数求出各个方案的优劣次序。 1.建立层次结构模型 该层主要有三个方面: 目标层 准则层 领域层(各种解决问题的措施和方案) 这里选择了一个旅游问题的层次分析模型来直观的展示三个层的关系: 如

    2024年02月04日
    浏览(38)
  • 【数学建模美赛 | 国赛必学模型算法精讲】层次分析法——模型原理及Matlab+Python双语言代码演示

    层次分析法 是 评价决策类 中一个比较常用的方法,很多留意美赛赛题的小伙伴们就会发现,在美赛EF类题目的历年O奖论文中,层次分析法出现的概率是非常高的。层次分析法呢一般是针对评价决策类的题目,让我们评价或选择一个可能更好、更优的政策及方案,那这样呢,

    2024年01月25日
    浏览(42)
  • 层次分析法(MATLAB)

    对之前的学习进行总结,整个比赛下来好像就用到了这个方法,最后也不知道对不对,反正最后还有点赶,就是很懵的那种,对于层次分析话的还是有点了解了,由于是纯小白,有错误的地方希望各位大佬能够指出。 目录 数据提取 归一化处理 判断矩阵 一致性检验  算术平

    2024年02月12日
    浏览(40)
  • 层次分析法(AHP)

    目录 目录 1 算法讲解 1.1解决评价类问题的一般步骤: 1.2 如何确定权重   1.2.1 判断矩阵的bug 1.2.2 一致性检验 1.2.3 计算权重  1.3 层次分析法具体步骤 1.3.1 建立层次结构 1.3.2 构造判断矩阵  1.3.3 一致性检验,计算权重 1.4 层次分析法局限性 2 代码 3 模型拓展 3.1 多个准则层

    2024年02月04日
    浏览(44)
  • 什么是结构分析法

    全面深刻认识经济社会现象,仅仅考察总量及其变化是不够的,还要考察经济社会系统中各构成部分及其对比关系的变动。 一、基本概念 二、主要的经济结构分析 三、示例 (二)产业结构。 (三)区域结构。

    2024年04月16日
    浏览(45)
  • 层次分析法

    人们常常面临一个由 相互关联、相互制约 的众 多因素 构成的复杂而往往 缺少定量数据 的系统; AHP是对一些较为复杂、较为模糊的问题作出决策的简易方法,特别适用于那些难于完全定量分析的问题; 美国运筹学家T. L. Saaty 教授于上世纪70 年代初期提出的一种简便、灵活

    2024年02月02日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包