本实验采用算符优先分析法,对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和:=等符号均未引入。
五. 实验调试情况及体会
略
标签:算符,grammer,ident,终结符,int,number,编译,vector,语法分析 From: https://www.cnblogs.com/hell0er/p/17428986.html