我们可以以下面的文法为例子进行算符优先分析:
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 }
运行结果