【编译原理】 实验三 LL(1)分析法(LL1分析表的自动生成)
写在前面
由于代码较长,csdn对文章总长度有字数限制,想只看完整代码的请移步另一篇博客。
https://blog.csdn.net/qq_46640863/article/details/125705891
目录
- 写在前面
- 一、实验内容
- 二、实验目的
- 三、实验分析
- 四、实验流程
- 4.1 整体流程
- 4.2 求FIRST
- 4.3 求FOLLOW
- 4.4 求分析表并分析输入串
- 五、实验代码
- 5.1 数据结构
- 5.2 部分核心算法设计
- 5.2.1 消除左递归与回溯并消除或
- 5.2.2 求解FIRST
- 5.2.3 求解FOLLOW
- 5.2.4 文法分析
- 5.3 完整程序
- 六、运行结果
- 七、实验感悟
一、实验内容
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)E→E+T∣TT→T∗F∣FF→(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 A→Aα∣β,对其消除左递归,可以将其改写为:
{ A → β A ′ A ′ → α A ′ ∣ ϵ \begin{cases} A\to \beta A'\\ A'\to \alpha A'|\epsilon \end{cases} {A→βA′A′→α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)E→TE′E′→+TE′∣ϵT→FT′T′→∗FT′∣ϵ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)E→TE′E′→+TE′E′→ϵT→FT′T′→∗FT′T′→ϵF→(E)F→i
为了LL(1)构造分析表 M M M,需要构造与文法相关的集合FIRST与FOLLOW。
FIRST( α \alpha α)表示 α \alpha α的所有可能推导的开头终结符或可能的空字 ϵ \epsilon ϵ。FIRST构造方法为反复运用下述规则,直到每个集合的FIRST集合不再变化为止。
1.若存在产生式 X → a X\to a X→a,且 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... X→Y...,且 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 X→Y1Y2...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... T→F...和 E → T . . . E\to T... E→T...,可得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→...Bβ,将FIRST( β \beta β)中除 ϵ \epsilon ϵ的元素加入FOLLOW( B B B)。
3.若存在产生式 A → . . . B A\to ...B A→...B或 A → . . . B β A\to ...B\beta A→...Bβ且 ϵ ∈ \epsilon\in ϵ∈FIRST( β \beta β),则把所有元素加到FOLLOW( B B B)。
构造FOLLOW集合时,按规则1,FOLLOW( E E E)= { # } \{ \#\} {#}。按规则2,由 E → T E ′ E\to TE' E→TE′,FOLLOW( T T T)= { + } \{+\} {+}。由 T → F T ′ T\to FT' T→FT′,FOLLOW( F F F)= { ∗ } \{*\} {∗},由 F → ( E ) F\to (E) F→(E),FOLLOW( E E E)= { ) , # } \{),\# \} {),#}。按规则3,由 E → T E ′ E\to TE' E→TE′,FOLLOW( E ′ E' E′)= { ) , # } \{),\#\} {),#}。由 E → T E ′ E\to TE' E→TE′且 E → ϵ E\to\epsilon E→ϵ,FOLLOW( T T T)= { + , ) , # } \{+,),\#\} {+,),#}。由 T → F T ′ T\to FT' T→FT′,FOLLOW( T ′ T' T′)= { + , ) , # } \{+,),\# \} {+,),#}。由 T → F T ′ T\to FT' T→FT′且 T ′ → ϵ T'\to \epsilon T′→ϵ,FOLLOW( F F F)= { ∗ , + , ) , # } \{*,+,),\# \} {∗,+,),#}
在构造完FIRST集合与FOLLOW集合后,可以构造分析表 M M M。对于文法中的每一个产生式,对每个终结符 a ∈ a\in a∈FIRST( A A A),把 A → α A\to \alpha A→α加入 M [ A , a ] M[A,a] M[A,a]中,其中 a ∈ a\in a∈FIRST( α \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' E→TE′ | E → T E ′ E\to TE' E→TE′ | ||||
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' T→FT′ | T → F T ′ T\to FT' T→FT′ | ||||
T ′ T' T′ | T ′ → ϵ T'\to\epsilon T′→ϵ | T ′ → ∗ F T ′ T'\to *FT' T′→∗FT′ | T ′ → ϵ T'\to\epsilon T′→ϵ | T ′ → ϵ T'\to\epsilon T′→ϵ | ||
F F F | F → i F\to i F→i | 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集合,自动分析生成建立分析表。输入待分析的文法,对其进行分析。整体流程如下图所示。
4.2 求FIRST
对字符或短语求FIRST集合时,往往需要用到递归。若在递归时发现 c c c字符的FIRST集合已经求解完毕,则直接返回。若当前字符 c c c是终结符,则FIRST集合为自己,返回。若不是终结符,则对其产生式进行遍历。若产生式中就是空字,则将空字加入FIRST集合。否则,遍历产生式的字符。递归的求解当前字符的FIRST集合,将该字符FIRST集合除了空字以外的元素加入。若该字符的FIRST集合存在空字,则遍历下一字符,如此循环。求解短语的FIRST集合的过程与求解某一非终结符的产生式的FIRST集合的流程是类似的。求FIRST集合的流程如下图。
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→Bβ且 ϵ ∈ \epsilon\in ϵ∈FIRST( β \beta β),将FOLLOW( A A A)加入FOLLOW( B B B)。每次都要反复进行此过程。
4.4 求分析表并分析输入串
构造分析表的流程与上文是一致的:若 a a a属于FIRST( A A A),把 A → a A\to a A→a加入到 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
表示文法的开始符,利用VN
与VT
分别表示非终结符与终结符。
字符的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 A→Aα∣β进行改写。为了方便程序的编写,所有符号都只用一个字符表示。从大写字母表中找一个没有被使用过的字母作为非终结符表示下式中的 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→βA′A′→α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'|enewstr = newstr + newch + "->" + alpha + newch + "|" + 'e';//e表示空字符episilonVT.insert('e');expression_withoutrecursion.push_back(newstr);}elseexpression_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|beta2string beta1 = str.substr(4, str.find('|') - 4);string beta2 = str.substr(str.find('|') + 1);newstr += newch + "->" + beta1 + '|' + beta2;expression_withoutback.push_back(newstr);}elseexpression_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->beta2string 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);}elseinputExpression.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++;elsebreak;//遍历完了当前语句后,将空字加入当前语句first集if (i == s.length())newset.insert('e');}firstSetS[s] = newset;
}
5.2.3 求解FOLLOW
求解FOLLOW集合的流程是迭代的。在调用初始化函数时,需要针对每一个字符反复的对其求解FOLLOW集合。
//求每个非终结符的followfor (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]];elseif (!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
六、运行结果
从文件中读取输入文法为算术表达式文法如下,这一文法存在左递归,且没有增广。
因此,需要识别终结符与非终结符,对其进行消除左递归、消除回溯、增广处理,运行结果如下。为了简化程序,用A
代替 E ′ E' E′,用B
代替 T ′ T' T′,用e
代替 ϵ \epsilon ϵ,其结果与第三章节推导的结果是完全一致的。
对于消除了”|”完成的文法,再次识别终结符与非终结符,并对每个非终结符输出其产生式右部。
在预处理工作完成后,即可对每个终结符与非终结符求解FIRST集合,求解非终结符的FOLLOW集合。其结果与第三章节的结果是完全一致的。
求解FIRST集合和FOLLOW集合完成后,构建分析表,其结果与第三章节的分析是一致的。
输入表达式 i ∗ i + i i*i+i i∗i+i,自动给出分析过程。
首先,将 # \# #与开始符 E E E压栈;根据 M [ E , i ] = E → T A M[E,i]=E\to TA M[E,i]=E→TA,将 E E E出栈,将 T A TA TA逆序后入栈;根据 M [ T , i ] = T → F B M[T,i]=T\to FB M[T,i]=T→FB,将 T T T出栈,将 F B FB FB逆序后入栈;根据 M [ F , i ] = F → i M[F,i]=F\to i M[F,i]=F→i,将 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分析表的自动生成)相关推荐
- 合工大 编译原理 实验三
合工大 编译原理 实验三 LR(1) 分析法 本项目使用c++实现,利用Windows API制作了简易的UI界面. 具体功能如下: 支持查看文法,项目族,LR(1) 分析表,句子归约过程. 可使用包 ...
- java编程实现算符优先分析法,编译原理实验三-算符优先分析法
编译原理实验3-算符优先分析法 #include #include #include #include #define SIZE 128 char priority[6][6]; //算符优先关系表数 ...
- 编译原理实验三 语义分析程序设计与实现
一.实验目的 在实现词法.语法分析程序的基础上,编写相应的语义子程序,进行语义处理,加深对语法制导翻译原理的理解,进一步掌握将语法分析所识别的语法范畴变换为某种中间代码(四元式)的语义分析方法,并完成 ...
- 编译原理实验三 LR(1)分析法
实验三 LR(1)分析法 构造 LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文 法识别的句子,了解 LR(K)分析方法是严格的从左向右扫描,和自底向上的 语法分析方法. 二.实验内 ...
- 编译原理 实验三 LR(1)分析法 Java
1. 实验目的 构造 LR(1)分析程序,利用它进行语法分析,判断给出的符号串是否为该文法识别的句子,了解 LR(K)分析方法是严格的从左向右扫描,和自底向上的语法分析方法. 2. 实验内容 对下列文 ...
- 编译原理实验报告一:PL0语言编译器分析(PL0,词法分析,语法分析,中间代码生成)
实验报告一:PL0语言编译器分析 一.实验目的 通过阅读与解析一个实际编译器(PL/0语言编译器)的源代码, 加深对编译阶段(包括词法分析.语法分析.语义分析.中间代码生成等)和编译系统软件结构的理解 ...
- 编译原理实验三:对完整程序进行词法分析并输出对应的二元组
实验要求 [任务介绍]根据给定源语言的构词规则,从任意字符串中识别出该语言所有的合法的单词符号,并以等长的二元组形式输出. [输入]字符串形式的源程序. [输出]单词符号所构成的串(流),单词以等长的 ...
- 编译原理实验三【中间代码生成程序设计】
基本要求: ①掌握中间代码生成的基本方法. ②掌握语法制导翻译模式. ③完成算术表达式的中间代码生成程序. 重点及难点:掌握语法制导翻译模式的核心思想和工作原理,在此基础上完成基于算数表达式的中间代码 ...
- 编译原理 - 实验三 - 递归下降语法分析器的调试及扩展
一. 语法分析介绍 语法分析是编译过程的核心部分,它的主要任务是按照程序语言的语法规则,从由词法分析输出的源程序符号串中识别出各类语法成分,同时进行语法检查,为语义分析和代码生成做准备.执行语法分析任 ...
最新文章
- 几个重要的 ASM Disk Groups 参数
- 艾伟也谈项目管理,项目做完了,总结一下
- 控制台程序转化为windows服务
- Javascript实现KMP算法
- 如何配置Ubuntu17的软件库(sortware update)
- c++11 lambda(匿名函数)
- (转)javascript 内存泄露工具使用
- Vue.js 还是 React?你会选择哪一个?为什么?
- 2019CVPR论文以及源码 awesome-object-proposals
- 基于ZigBee的远程温度监测系统.
- SpringBoot 发送电子邮件
- hadoop 查看文件大小以及文件数
- 论OIer谈恋爱的必要性
- Roson的Qt之旅#97 Qt编译报错-Could not determine which “make“ command to run
- 梯度下降法和最速下降法区别
- SharpDevelop浅析_1_AddInTree
- Android 各种屏幕截图方法
- Java 报错: Cannot deserialize instance of `com.ruoyi.web.entity.xxxEntity` out of START_ARRAY toke
- 前端微信小程序生鲜类仿爱鲜蜂微信小程序
- 杨辉三角(直角形状)
热门文章
- webrtc服务器压测工具使用
- 四种常见背包问题整理
- 全国计算机一级WORD第三套,全国计算机等级考试一级WPSOffice练习题及答案第三套.pdf...
- PyCharm中光标变粗的解决方法
- onscroll在移动端的使用
- 能源价格风险管理matlab源代码 经济物理学、电价、风险管理、均值回归
- 浅析公关中的“道”与“术”
- CrossWalk问题
- opensuse安装face_recognition全记录
- 国外十几种国外撸美金项目适合小白们