《编译原理》实验三:自下而上语法分析(算符优先分析法)

发布时间 2023-06-01 10:46:09作者: Hell0er

本实验采用算符优先分析法,对PL/0语言的算术运算进行语法分析。

本程序由我个人独立完成,代码为C++98,因此可能较丑陋,且不能保证完全正确,还请见谅 (¯﹃¯)


一. 设计思想

1. 文法

因实验二中的文法不是算符优先文法,所以本次实验采用了新的文法。

(1)EBNF

<表达式> ::= [+|-]<项>{<加法运算符> <项>}

<项> ::= <因子>{<乘法运算符> <因子>}

<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’

<加法运算符> ::= +|-

<乘法运算符> ::= *|/

(2)对EBNF中的各对象简称

E:表达式

T:项

F:因子

b:标识符

z:无符号整数

(3)将文法改写成常规的产生式形式

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

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

F -> (E)|b|z

2. FIRSTVT集和LASTVT集

FIRSTVT(E) = {(,b,z,+,-,*,/} LASTVT(E) = {),b,z,+,-,*,/}
FIRSTVT(T) = {(,b,z,*,/} LASTVT(T) = {),b,z,*,/}
FIRSTVT(F) = {(,b,z} LASTVT(F) = {},b,z}

满足算符优先文法的条件,是算符优先文法。

3.终结符之间的优先关系定义

  • a = b:a的优先级等于b
  • a < b:a的优先级小于b
  • a > b:a的优先级大于b

4. 优先关系表

  + - * / ( ) b z #
+ > > < < < > < < >
- > > < < < > < < >
* > > > > < > < < >
/ > > > > < > < < >
( < < < < < = < <  
) > > > > >   >   >
b > > > >   >     >
z > > > >   >     >
# < < < < <   < < =

二. 算法流程

三. 源程序

  1 #include <iostream>
  2 #include <string>
  3 #include <vector>
  4 #include <map>
  5 #include <stack>
  6 #include <set>
  7 using namespace std;
  8 
  9 const int nt_num=10;    // 非终结符数目
 10 const int t_num=10;     // 终结符数目
 11 
 12 vector<string> grammer;   // 文法
 13 vector<vector<char> > FirstVT(nt_num,vector<char>(0));
 14 vector<vector<char> > LastVT(nt_num,vector<char>(0));   // FirstVT集和LastVT集
 15 vector<vector<string> > input;   // 输入(','两边)
 16 
 17 vector<char> nonterminal,terminal;   // 非终结符数组 终结符数组
 18 map<char,int> ntchar2idx;   // 非终结符转下标
 19 map<char,int> tchar2idx;   // 终结符转下标
 20 vector<vector<int> > table(nt_num,vector<int>(nt_num,-5));   // 优先关系表:>为1;=为0;<为-1;5为error
 21 stack<char> s1,s2;   // s2作为临时存储
 22 
 23 void getNonterminal(string s)   // 得到非终结符
 24 {
 25     nonterminal.push_back(s[0]);   // 文法的首字母一定为非终结符,且'->'后不用考虑
 26 }
 27 
 28 void getTerminal(string s,set<char>& temp_terminal)   // 得到终结符,当s="over"时说明可将临时set存入永久vector了
 29 {
 30     if (s=="over")
 31     {
 32         for (set<char>::iterator it=temp_terminal.begin();it!=temp_terminal.end();++it)
 33         {
 34             terminal.push_back(*it);
 35         }
 36         terminal.push_back('#');
 37     }
 38     else
 39     {
 40         for (int i=3;i<(int)s.size();++i)   // '->'后除'|'和非终结符的均为终结符
 41         {
 42             if (s[i]!='|' && (s[i]<'A' || s[i]>'Z'))
 43             {
 44                 temp_terminal.insert(s[i]);
 45             }
 46         }
 47     }
 48     return;
 49 }
 50 
 51 bool isTerminal(const char& ch)   // 判断是否为终结符
 52 {
 53     for (int i=0;i<(int)terminal.size();++i)
 54     {
 55         if (ch==terminal[i])
 56         {
 57             return true;
 58         }
 59     }
 60     return false;
 61 }
 62 
 63 void char2idx()   // (非)终结符转下标
 64 {
 65     char ch;
 66     for (int i=0;i<(int)nonterminal.size();++i)
 67     {
 68         ch=nonterminal[i];
 69         ntchar2idx[ch]=i+1;
 70     }
 71     for (int i=0;i<(int)terminal.size();++i)
 72     {
 73         ch=terminal[i];
 74         tchar2idx[ch]=i+1;
 75     }
 76 }
 77 
 78 void createTable()   // 创建优先关系表
 79 {
 80     if (isTerminal('(') && isTerminal(')'))   // 存在终结符'('和')',则优先关系表中为=,0
 81     {
 82         table[tchar2idx['(']][tchar2idx[')']]=0;
 83     }
 84     for (int i=0;i<grammer.size();++i)
 85     {
 86         for (int j=3;j<grammer[i].size();++j)
 87         {
 88             if (grammer[i].size()>4 && isTerminal(grammer[i][j]))
 89             {
 90                 int id=tchar2idx[grammer[i][j]];
 91                 if (!isTerminal(grammer[i][j-1]))   // 前一个字符为非终结符,考虑LASTVT集,>,1
 92                 {
 93                     char ch1=grammer[i][j-1];
 94                     for (int k=0;k<LastVT[ntchar2idx[ch1]].size();++k)
 95                     {
 96                         char ch2=LastVT[ntchar2idx[ch1]][k];
 97                         table[tchar2idx[ch2]][id]=1;
 98                     }
 99                 }
100                 if (!isTerminal(grammer[i][j+1]))   // 后一个字符为非终结符,考虑FIRSTVT集,<,-1
101                 {
102                     char ch1=grammer[i][j+1];
103                     for (int k=0;k<FirstVT[ntchar2idx[ch1]].size();++k)
104                     {
105                         char ch2=FirstVT[ntchar2idx[ch1]][k];
106                         table[id][tchar2idx[ch2]]=-1;
107                     }
108                 }
109             }
110         }
111     }
112     char head=grammer[0][0];   // 起始符
113     // '#'
114     table[tchar2idx['#']][tchar2idx['#']]=0;
115     int id=ntchar2idx[head];
116     for (int i=0;i<LastVT[id].size();++i)
117     {
118         char ch=LastVT[id][i];
119         table[tchar2idx[ch]][tchar2idx['#']]=1;
120     }
121     for (int i=0;i<FirstVT[id].size();++i)
122     {
123         char ch=FirstVT[id][i];
124         table[tchar2idx['#']][tchar2idx[ch]]=-1;
125     }
126     return;
127 }
128 
129 bool ERROR(int pos)
130 {
131 //    cout<<"wrong_pos = "<<pos<<endl;
132     return false;
133 }
134 
135 bool opPriorityAnalyse()   // 算符优先分析程序
136 {
137     s1.push('#');
138     int i=0;
139     string t0;
140     char t1;
141     while (!s1.empty() && i<(int)input.size())
142     {
143         t0=input[i][0];
144         if (t0=="ident")
145         {
146             t1='b';
147         }
148         else if (t0=="number")
149         {
150             t1='z';
151         }
152         else
153         {
154             t1=input[i][1][0];   //非number,右边一定只有一个字符,取[0]即可
155         }
156 
157         if (!isTerminal(s1.top()))   // 栈顶为非终结符,则要跳过它(即压入临时栈s2)
158         {
159             s2.push(s1.top());
160             s1.pop();
161         }
162         int x=tchar2idx[s1.top()],y=tchar2idx[t1];
163         if (table[x][y]==-1 || table[x][y]==0)   // < 或 =,进栈,++i
164         {
165             if (!s2.empty())
166             {
167                 s1.push(s2.top());
168                 s2.pop();
169             }
170             s1.push(t1);
171             ++i;
172         }
173         else if (table[x][y]==1)   // >,归约
174         {
175             int last=x,cur=y;
176             while (table[last][cur]!=-1)   // 直到找到<
177             {
178                 if (table[last][cur]==5) return ERROR(0);   // 找到了ERROR
179                 s2.push(s1.top());
180                 s1.pop();
181                 if (!isTerminal(s1.top()))   // 栈顶为非终结符,则要跳过它(即压入临时栈s2)
182                 {
183                     s2.push(s1.top());
184                     s1.pop();
185                 }
186                 cur=last,last=tchar2idx[s1.top()];
187             }
188             // 对s2中的符号归约
189             string str="";
190             while (!s2.empty())
191             {
192                 str+=s2.top();
193                 s2.pop();
194             }
195             if (str.size()==3 && (str[1]=='+' || str[1]=='-'))
196             {
197                 s1.push('E');
198             }
199             else if (str.size()==3 && (str[1]=='*' || str[1]=='/'))
200             {
201                 s1.push('T');
202             }
203             else if ((str.size()==3 && str[0]=='(') || (str.size()==1 && (str[0]=='b' || str[0]=='z')))
204             {
205                 s1.push('F');
206             }
207             else
208             {
209                 return ERROR(1);
210             }
211         }
212         else   // ERROR
213         {
214             return ERROR(5);
215         }
216     }
217     if (s1.size()==3 && s2.empty())
218     {
219         return true;
220     }
221     else
222     {
223         return ERROR(2);
224     }
225 }
226 
227 vector<char> getFirstVT(char head)   // 求非终结符head的FIRSTVT
228 {
229     if (FirstVT[ntchar2idx[head]].size())
230     {
231         return FirstVT[ntchar2idx[head]];   // 已经求过了,直接返回
232     }
233     set<char> vt;
234     for (int i=0;i<grammer.size();++i)
235     {
236         if (grammer[i][0]==head)
237         {
238             if (grammer[i].size()==4 && grammer[i][3]>='A' && grammer[i][3]<='Z')   // 非终结符->非终结符
239             {
240                 vector<char> temp=getFirstVT(grammer[i][3]);
241                 for (int j=0;j<temp.size();++j)   // 存入
242                 {
243                     vt.insert(temp[j]);
244                 }
245             }
246             else if (grammer[i].size()==4)   // 非终结符->终结符
247             {
248                 vt.insert(grammer[i][3]);
249             }
250             else   // 非终结符->终结符与非终结符的组合
251             {
252                 for (int j=3;j<grammer[i].size();++j)
253                 {
254                     if (isTerminal(grammer[i][j]))
255                     {
256                         vt.insert(grammer[i][j]);
257                         break;
258                     }
259                 }
260             }
261         }
262     }
263     vector<char> ret;
264     for (set<char>::iterator it=vt.begin();it!=vt.end();++it)
265     {
266         ret.push_back(*it);
267     }
268     return ret;
269 }
270 
271 vector<char> getLastVT(char head)   // 求非终结符head的LASTVT
272 {
273     if (LastVT[ntchar2idx[head]].size())
274     {
275         return LastVT[ntchar2idx[head]];   // 已经求过了,直接返回
276     }
277     set<char> vt;
278     for (int i=0;i<grammer.size();++i)
279     {
280         if (grammer[i][0]==head)
281         {
282             if (grammer[i].size()==4 && grammer[i][3]>='A' && grammer[i][3]<='Z')   // 非终结符->非终结符
283             {
284                 vector<char> temp=getLastVT(grammer[i][3]);
285                 for (int j=0;j<temp.size();++j)   // 存入
286                 {
287                     vt.insert(temp[j]);
288                 }
289             }
290             else if (grammer[i].size()==4)   // 非终结符->终结符
291             {
292                 vt.insert(grammer[i][3]);
293             }
294             else   // 非终结符->终结符与非终结符的组合
295             {
296                 for (int j=grammer[i].size()-1;j>=3;--j)
297                 {
298                     if (isTerminal(grammer[i][j]))
299                     {
300                         vt.insert(grammer[i][j]);
301                         break;
302                     }
303                 }
304             }
305         }
306     }
307     vector<char> ret;
308     for (set<char>::iterator it=vt.begin();it!=vt.end();++it)
309     {
310         ret.push_back(*it);
311     }
312     return ret;
313 }
314 
315 void inputGrammer()   // 输入文法
316 {
317     string s,str="";
318     set<char> temp_terminal;   // 临时的termina数组,定义为set防止重复
319     cout<<"请输入文法句数:";
320     int T;cin>>T;
321     cout<<"请输入文法:"<<endl;
322     while (T--)
323     {
324         cin>>s;
325         getNonterminal(s),getTerminal(s,temp_terminal);   // 对每句文法分析得到非终结符和终结符
326         char head=s[0];
327         for (int i=3;i<s.size();++i)   // 将'|'左右的子文法拆开
328         {
329             if (s[i]=='|')
330             {
331                 str="->"+str;
332                 str=head+str;
333 //                cout<<"str="<<str<<endl;
334                 grammer.push_back(str);
335                 str="";
336                 continue;
337             }
338             str+=s[i];
339         }
340         str="->"+str;
341         str=head+str;
342 //        cout<<"str="<<str<<endl;
343         grammer.push_back(str);
344         str="";
345     }
346     getTerminal("over",temp_terminal);
347 
348     char2idx();
349 
350     int id;
351     vector<char> v;
352     for (int i=0;i<nonterminal.size();++i)   // 逐个非终结符求FIRSTVT集和LASTVT集
353     {
354         cout<<nonterminal[i]<<endl;
355         id=ntchar2idx[nonterminal[i]];
356         v=getFirstVT(nonterminal[i]);
357         for (int j=0;j<v.size();++j)
358         {
359             FirstVT[id].push_back(v[j]);
360         }
361         v=getLastVT(nonterminal[i]);
362         for (int j=0;j<v.size();++j)
363         {
364             LastVT[id].push_back(v[j]);
365         }
366     }
367     createTable();
368     return;
369 }
370 
371 int main()
372 {
373     inputGrammer();
374 
375     cout<<"请输入词法分析的结果:"<<endl;
376     string str;
377     vector<string> vec;
378     while (cin>>str)
379     {
380         int pos=str.find(',');
381         vec.clear();
382         vec.push_back(str.substr(1,pos-1));
383         vec.push_back(str.substr(pos+1,str.size()-pos-2));
384         input.push_back(vec);
385     }
386     vec.clear();
387     vec.push_back("#");vec.push_back("#");
388     input.push_back(vec);
389 
390     if (opPriorityAnalyse())
391     {
392         cout<<"Yes,it is correct."<<endl;
393     }
394     else
395     {
396         cout<<"No,it is wrong."<<endl;
397     }
398 
399     return 0;
400 }
View Code

四. 调试数据

1.样例输入:

1 (lparen,()
2 (ident,a)
3 (plus,+)
4 (number,15)
5 (rparen,))
6 (times,*)
7 (ident,b)

样例输出:

Yes,it is correct.

图片展示:

2.样例输入:

 1 (number,0)
 2 (plus,+)
 3 (number,10)
 4 (times,*)
 5 (ident,b)
 6 (minus,-)
 7 (lparen,()
 8 (ident,z)
 9 (slash,/)
10 (number,3)
11 (rparen,))

样例输出:

Yes,it is correct.

图片展示:

3.样例输入:

 1 (lparen,()
 2 (lparen,()
 3 (ident,a)
 4 (plus,+)
 5 (number,3)
 6 (rparen,))
 7 (times,*)
 8 (lparen,()
 9 (number,0)
10 (minus,-)
11 (ident,b)
12 (rparen,))
13 (minus,-)
14 (ident,c)
15 (slash,/)
16 (number,0)
17 (plus,+)
18 (lparen,()
19 (ident,a)
20 (times,*)
21 (ident,d)
22 (slash,/)
23 (ident,e)
24 (plus,+)
25 (ident,f)
26 (rparen,))
27 (rparen,))

样例输出:

Yes,it is correct.

图片展示:

4.样例输入:

1 (lparen,()
2 (ident,a)
3 (plus,+)
4 (number,15)
5 (rparen,))
6 (times,*)

样例输出:

No,it is wrong.

图片展示:

5.样例输入:

1 (ident,a)
2 (plus,+)
3 (number,15)
4 (rparen,))
5 (times,*)
6 (ident,b)

样例输出:

No,it is wrong.

图片展示:

6.样例输入:

 1 (number,0)
 2 (plus,+)
 3 (number,10)
 4 (times,*)
 5 (ident,b)
 6 (lparen,()
 7 (ident,z)
 8 (slash,/)
 9 (number,3)
10 (rparen,))

样例输出:

No,it is wrong.

图片展示:

注:文法只包含简单的+、-、*、/运算等,而像实验1中的const和:=等符号均未引入。

五. 实验调试情况及体会