我們可以以下面的文法為例子進行算符優先分析:
E→E+T|T
T→T*F|F
F→(E)|i
FIRSTVT和LASTVT的構建
首先我們要通過文法規則來產生相應的FIRSTVT和LASTVT集,具體的構建規則如下:
FIRSTVT:
若出現 E→a... 或 E→Aa 的情況,則 a∈FIRSTVT【E】,即產生式右部的第一個終結符屬於左部非終結符的FIRSTVT集
若出現 E→A... 的情況,則 FIRSTVT【A】∈FIRSTVT【E】,即產生式右部開頭若是非終結符的情況下,該終結符的FIRSTVT屬於左部非終結符的FIRSTVT集
LASTVT:
若出現 E→...a 或 E→...aA 的情況,則 a∈LASTVT【E】,即產生式右部倒數第一個終結符屬於左部非終結符的FIRSTVT集
若出現 E→...A 的情況,則 LASTVT【A】∈LASTVT【E】,集產生式右部尾部若是非終結符的情況下,該終結符的LASTVT屬於左部非終結符的LASTVT集
通過以上方法得到的FIRSTVT和LASTVT集如下:
FIRSTVT:
FIRSTVT【E】={ ( , i , + , * }
FIRSTVT【T】 = { ( , i , * }
FIRSTVT【F】 = { ( , i }
LASTVT:
LASTVT【E】 = { ) , i , + , * }
LASTVT【T】 = { ) , i , * }
LASTVT【F】 = { ) , i }
算符優先關系表的構造
得到了文法的FIRSTVT和LASTVT集以后,我們需要通過它來得到算符優先關系表,具體構造方法如下:
若出現 E→...ab... 或者 E→....aAb... 的情況:a = b
若出現 E→...aA.. 且 b∈FIRSTVT【A】的情況:a < b
若出現 E→...Ab...且 a∈LASTVT【A】的情況:a > b
用此方法構造的算符優先分析表如下所示:
i | + | * | ( | ) | # | |
i | > | > | > | > | ||
+ | < | > | < | < | > | > |
* | < | > | > | < | > | > |
( | < | < | < | < | = | |
) | > | > | > | > | ||
# | < | < | < | < | = |
注:在討論#與其他終結符的優先級時,可以添加E→#E#來完成
算符優先分析
算符優先分析采用的是移進-歸約法,取符號棧中最左素短語的終結符,並將其與輸入串頭的元素進行優先級比較,若優先關系為 ">" ,則進行歸約,若為 "<" 或 "=" 則將輸入串頭元素壓入符號棧。
我們以i+i*i為例來進行算符優先分析:
符號棧 | 輸入串 | 動作# |
# | i+i*i# | 移進 |
#i | +i*i# | 歸約 |
#E | +i*i# | 移進 |
#E+ | i*i# | 移進 |
#E+i | *i# | 歸約 |
#E+E | *i# | 移進 |
#E+E* | i# | 移進 |
#E+E*i | # | 歸約 |
#E+E*E | # | 歸約 |
#E+E | # | 歸約 |
#E | # | 結束 |
注:算符優先分析中不關心非終結符的優先關系,因此我們在歸約的時候可以不用考慮字符串被歸約到了哪個非終結符。
參考代碼
用c#進行實現,代碼冗余極多,效率很低,泛用性不足,各種考慮不全面的情況也很多,大家權當做個參考。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 算符優先分析 8 { 9 10 class Program 11 { 12 /*-------------------------------變量聲明---------------------------------------------------------------------------------------*/ 13 static string effectValue = "i+*()#"; //有效的字符集合 14 static List<string> grammarList = new List<string>(); //文法列表 15 static Dictionary<string, List<string>> grammarDic = new Dictionary<string, List<string>>(); //文法字典 16 static Dictionary<string, List<string>>.KeyCollection vertex; //非終結符集合 17 static Dictionary<string, string> firstVT = new Dictionary<string, string>(); //firstVT集 18 static Dictionary<string, string> lastVT = new Dictionary<string, string>(); //lastVT集 19 static char[,] priorityTable = new char[7,7]; //算符優先關系表 20 static string message; //要分析的字符串 21 static MyStack stack = new MyStack(); 22 23 24 /*----------------------------主函數--------------------------------------------------------------------------------------------*/ 25 static void Main(string[] args) 26 { 27 inputGrammar(); //獲得文法字符串 28 getVT(); //獲得firstVT和lastVT 29 printVT(); //輸出firstVT和lastVT 30 getPriorityTable();//得到優先關系表 31 printPriorityTable();//打印優先關系表 32 inputMessage(); //獲得要分析的字符串 33 34 //判斷字符串是否符合要求 35 if (judgeMessage()) 36 //進行算符優先分析 37 if (analyze()) 38 Console.WriteLine("分析成功!該句子符合算符優先。"); 39 else Console.WriteLine("分析失敗!該句子不符合算符優先。"); 40 } 41 42 /*---------------------------------------輸入數據---------------------------------------------------------------------*/ 43 static void inputGrammar() 44 { 45 Console.WriteLine("該系統支持的終結符有:i,+,*,(,),請勿輸入其他終結符!"); 46 Console.WriteLine("請輸入要分析的文法:(在單獨一行輸入#結束輸入)"); 47 Console.WriteLine("如:E→E+T|T\nT→T*F|F\nF→(E)|i\n#\n"); 48 while (true) 49 { 50 string str = Console.ReadLine(); 51 if (str.Equals("#")) 52 break; 53 else grammarList.Add(str); 54 } 55 //將文法轉存為文法字典保存 56 foreach (string str in grammarList) 57 { 58 List<string> l1 = new List<string>(); 59 string[] s1 = str.Split('→'); 60 string[] s2 = s1[1].Split('|'); 61 foreach (string s in s2) 62 l1.Add(s); 63 grammarDic.Add(s1[0], l1); 64 } 65 vertex = grammarDic.Keys; //將文法字典的key值給vector 66 } 67 68 /*---------------------------------------得到firstVT和lastVT----------------------------------------------------------*/ 69 static void getVT() 70 { 71 //第一遍遍歷,將第一個非終結符和在串頭的終結符加入firstVT 72 foreach (string v in vertex) 73 { 74 List<string> l = grammarDic[v]; 75 foreach (string s in l) 76 { 77 getFirstVT(s, v); 78 getLastVT(s, v); 79 } 80 } 81 trimFirstVT(); 82 trimLastVT(); 83 84 } 85 86 //得到firstVT 87 static void getFirstVT(string s, string v) 88 { 89 //從前往后,找合適字符加入firstVT 90 for (int i = 0; i < s.Length; i++) 91 { 92 string t = Convert.ToString(s[i]); 93 //如果是非終結符,且在字符串最前,將其加入firstVT 94 if (grammarDic.ContainsKey(t) && i == 0) 95 if (firstVT.ContainsKey(v)) 96 if (!firstVT[v].Contains(t)) 97 firstVT[v] += t; 98 else firstVT.Add(v, t); 99 //將第一個終結符加入firstVT 100 if (!grammarDic.ContainsKey(t)) 101 { 102 if (firstVT.ContainsKey(v)) 103 firstVT[v] += t; 104 else firstVT.Add(v, t); 105 break; 106 } 107 } 108 } 109 110 //得到lastVT 111 static void getLastVT(string s, string v) 112 { 113 //從后往前,找合適字符加入lastVT 114 for (int i = s.Length - 1; i >= 0; i--) 115 { 116 string t = Convert.ToString(s[i]); 117 //如果是非終結符,且在字符串最后,將其加入lastVT 118 if (grammarDic.ContainsKey(t) && i == s.Length - 1) 119 if (lastVT.ContainsKey(v)) 120 if (!lastVT[v].Contains(t)) 121 lastVT[v] += t; 122 else lastVT.Add(v, t); 123 //將最后一個終結符加入firstVT 124 if (!grammarDic.ContainsKey(t)) 125 { 126 if (lastVT.ContainsKey(v)) 127 lastVT[v] += t; 128 else lastVT.Add(v, t); 129 break; 130 } 131 } 132 } 133 134 //將firstVT中的終結符去掉 135 static void trimFirstVT() 136 { 137 //逆序遍歷終結符集(必須逆序) 138 foreach (string v in vertex.Reverse()) 139 { 140 string s1 = firstVT[v]; 141 for (int i = 0; i < s1.Length; i++) 142 { 143 string t = Convert.ToString(s1[i]); 144 //若firstVT集中含有終結符,進行整理 145 if (grammarDic.ContainsKey(t)) 146 { 147 firstVT[v] = firstVT[v].Replace(t, ""); //刪除此終結符 148 string s2 = firstVT[t]; 149 //將此終結符的firstVT集添加進來 150 for (int j = 0; j < s2.Length; j++) 151 { 152 if (!s1.Contains(s2[j]) && !grammarDic.ContainsKey(Convert.ToString(s2[j]))) 153 firstVT[v] += s2[j]; 154 } 155 } 156 } 157 } 158 } 159 160 //將firstVT中的終結符去掉,過程同trimFirstVT() 161 static void trimLastVT() 162 { 163 foreach (string v in vertex.Reverse()) 164 { 165 string s1 = lastVT[v]; 166 for (int i = 0; i < s1.Length; i++) 167 { 168 string t = Convert.ToString(s1[i]); 169 if (grammarDic.ContainsKey(t)) 170 { 171 lastVT[v] = lastVT[v].Replace(t, ""); 172 string s2 = lastVT[t]; 173 for (int j = 0; j < s2.Length; j++) 174 { 175 if (!s1.Contains(s2[j]) && !grammarDic.ContainsKey(Convert.ToString(s2[j]))) 176 lastVT[v] += s2[j]; 177 } 178 } 179 } 180 } 181 } 182 183 /*------------------------------------------------------打印firstVT和lastVT------------------------------------------------------*/ 184 static void printVT() 185 { 186 //輸出firstVT 187 Console.WriteLine("\nfirstVT:"); 188 foreach (string s in firstVT.Keys) 189 { 190 Console.Write("firstVT " + s + " : "); 191 for (int i = 0; i < firstVT[s].Length; i++) 192 Console.Write(firstVT[s][i] + " "); 193 Console.WriteLine(""); 194 } 195 //輸出lastVT 196 Console.WriteLine("lastVT:"); 197 foreach (string s in lastVT.Keys) 198 { 199 Console.Write("lastVT " + s + " : "); 200 for (int i = 0; i < lastVT[s].Length; i++) 201 Console.Write(lastVT[s][i] + " "); 202 Console.WriteLine(); 203 } 204 } 205 206 /*--------------------------------------------------------算符優先關系表----------------------------------------------------*/ 207 //得到算符優先關系表 208 static void getPriorityTable() 209 { 210 //添加文法:E→#E# 211 string str = vertex.FirstOrDefault(); 212 grammarDic[str].Add("#" + str + "#"); 213 int i1, j1; 214 //對優先關系表進行初始化 215 for (int i = 1; i <= effectValue.Length; i++) 216 { 217 priorityTable[0,i] = effectValue[i - 1]; 218 priorityTable[i,0] = effectValue[i - 1]; 219 } 220 //對產生式右部進行遍歷 221 foreach (string v in vertex) 222 { 223 List<string> g = grammarDic[v]; 224 foreach (string s in g) 225 { 226 for (int i = 0; i < s.Length; i++) 227 { 228 string t = Convert.ToString(s[i]); 229 //若只有一個非終結符,則#=# 230 if (s.Length == 1) 231 { 232 if (grammarDic.ContainsKey(t)) 233 priorityTable[6, 6] = '='; 234 } 235 else 236 { 237 if (i > 0) 238 { 239 string tp = Convert.ToString(s[i - 1]); 240 //出現ab型的產生式,則a=b 241 if ((!grammarDic.ContainsKey(tp)) && (!grammarDic.ContainsKey(t))) 242 { 243 i1 = effectValue.IndexOf(tp); 244 j1 = effectValue.IndexOf(t); 245 priorityTable[i1, j1] = '='; 246 } 247 //出現aA型的產生式,則a<firstVT[A] 248 if ((!grammarDic.ContainsKey(tp)) && grammarDic.ContainsKey(t)) 249 { 250 i1 = effectValue.IndexOf(tp)+1; 251 string fir = firstVT[t]; 252 for (int j = 0; j < fir.Length; j++) 253 { 254 j1 = effectValue.IndexOf(fir[j])+1; 255 priorityTable[i1, j1] = '<'; 256 } 257 } 258 //出現Aa型的產生式,則lastVT[A]>a 259 if(grammarDic.ContainsKey(tp) && (!grammarDic.ContainsKey(t))) 260 { 261 j1 = effectValue.IndexOf(t) + 1; 262 string lat = lastVT[tp]; 263 for(int j=0;j<lat.Length;j++) 264 { 265 i1 = effectValue.IndexOf(lat[j]) + 1; 266 priorityTable[i1, j1] = '>'; 267 } 268 } 269 if(i<s.Length-1) 270 { 271 string tn = Convert.ToString(s[i + 1]); 272 //出現aAb型的產生式,則a=b 273 if((!grammarDic.ContainsKey(tp)) && grammarDic.ContainsKey(t) && (!grammarDic.ContainsKey(tn))) 274 { 275 i1 = effectValue.IndexOf(tp)+1; 276 j1 = effectValue.IndexOf(tn)+1; 277 priorityTable[i1, j1] = '='; 278 } 279 } 280 } 281 } 282 } 283 } 284 } 285 } 286 287 //輸出算符優先關系表 288 static void printPriorityTable() 289 { 290 Console.WriteLine("\n算符優先關系表如下:"); 291 for (int i = 0; i < 7; i++) 292 { 293 for (int j = 0; j < 7; j++) 294 Console.Write(priorityTable[i,j] + "\t"); 295 Console.WriteLine("\n"); 296 } 297 } 298 299 /*-------------------------------------------算符優先分析-----------------------------------------------------------------*/ 300 //得到要分析的字符串 301 static void inputMessage() 302 { 303 Console.WriteLine("\n該分析器只能識別i,+,*,(,),#,請勿輸入其他字符!"); 304 Console.WriteLine("請輸入要分析的字符串,用#結尾(如i+i*i#)"); 305 message = Console.ReadLine(); 306 } 307 308 //檢測輸入的字符串是否符合要求 309 static Boolean judgeMessage() 310 { 311 //檢測輸入的字符串是否合法 312 bool isLegal = true; 313 bool flag = true; 314 if (message[message.Length - 1] != '#') 315 { 316 Console.WriteLine("字符串必須以#結尾!"); 317 flag = false; 318 } 319 for (int i = 0; i < message.Length; i++) 320 if (!effectValue.Contains(message[i])) 321 isLegal = false; 322 if (!isLegal) 323 { 324 Console.WriteLine("該字符串中含有非法字符!"); 325 flag = false; 326 } 327 328 return flag; 329 } 330 331 //進行算符優先分析 332 static Boolean analyze() 333 { 334 bool flag = true; 335 stack.push("#"); //對棧進行初始化 336 337 //對待處理串中的每個字符進行遍歷 338 for (int i = 0; i < message.Length; ) 339 { 340 string s = stack.getString(); //獲得棧中數據 341 int j = s.Length - 1; 342 343 //尋找棧中第一個終結符 344 while (grammarDic.ContainsKey(s[j].ToString()) || s[j] == 'E') 345 { 346 j--; 347 } 348 349 //獲取棧中第一個終結符,與字符串頭元素在優先表中的位置 350 int i1 = effectValue.IndexOf(s[j]) + 1; 351 int j1 = effectValue.IndexOf(message[i]) + 1; 352 353 //優先表值進行相應的操作 354 switch (priorityTable[i1, j1]) 355 { 356 case '<': moveIn(i); i++; break; 357 case '=': moveIn(i); i++; break; 358 case '>': flag = reduction(i,j); break; 359 default: break; 360 } 361 362 //分析完成輸出結果 363 if (stack.getString().Equals("#E") && i >= message.Length-1) 364 { 365 Console.WriteLine(stack.getString() + "\t\t" + message.Substring(i) + "\t\t" + "分析完成"); 366 break; 367 } 368 //分析失敗退出循環 369 if (!flag) 370 break; 371 } 372 return flag; 373 } 374 375 //移進操作 376 static void moveIn(int i) 377 { 378 Console.WriteLine(stack.getString() + "\t\t" + message.Substring(i) + "\t\t移進"); 379 stack.push(message[i].ToString()); 380 } 381 382 //歸約操作 383 static Boolean reduction(int n,int i) 384 { 385 string s = stack.getString(); 386 bool flag = true; 387 int k = i-1; 388 389 //遍歷每一個文法 390 foreach (string v in vertex) 391 { 392 string c = s[i].ToString(); 393 List<string> list = grammarDic[v]; 394 foreach (string g in list) 395 { 396 if (g.Contains(c)) 397 { 398 Console.WriteLine(s + "\t\t" + message.Substring(n) + "\t\t規約"); 399 if (c == "i") 400 { 401 //防止多個i連一起的情況發生 402 if (s[i - 1] == 'i') 403 flag = false; 404 else 405 { 406 stack.pop(); 407 stack.push("E"); 408 } 409 } 410 else if (s.Length > g.Length) 411 { 412 for (int j = 0; j < g.Length; j++) 413 stack.pop(); 414 stack.push("E"); 415 } 416 //開頭不能為運算符 417 else if (s[i - 1] != 'i') 418 flag = false; 419 } 420 } 421 } 422 return flag; 423 } 424 425 /*----------------------------------------------------堆棧操作--------------------------------------------------------------*/ 426 //系統自帶的堆棧不是很好用,於是我自己寫了一個 427 class MyStack 428 { 429 string data; 430 int top; 431 public MyStack() 432 { 433 data = ""; 434 top = -1; 435 } 436 public void push(string s) 437 { 438 data += s; 439 top++; 440 } 441 public string pop() 442 { 443 data = data.Substring(0, data.Length - 1); 444 top--; 445 return data; 446 } 447 public string getString() 448 { 449 return data; 450 } 451 } 452 } 453 }
運行結果