本来这次想好好写一下博客的...结果耐心有限,又想着烂尾总比断更好些。于是还是把后续代码贴上。不过后续代码是继续贴在BNF容器里面的...可能会显得有些臃肿。但目前管不了那么多了。先贴上来吧hhh。说不定哪天觉得羞耻又改了呢。参考资料建议参考《编译器设计》一书。
目前完成进度 : 目前已经完成了表驱动,通过函数输出这个Action 和 Goto表。然后使用者就可以根据两个表来进行LR(1)语法分析。且经过比对,发现和书上的例子(括号语法)是完全吻合的。
1 package cn.vizdl.LR1.version3;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Scanner;
8 import java.util.Set;
9
10 /*
11 项目名 : LR(1) parser generator (LR(1)语法分析器生成器)
12 项目分析 :
13 输入 : 输入某文件内存地址。且内部采用 <Expr> ::= <Term> + <Factor>; 的结构输入的LR(1)语法。
14 这里的仅支持 BNF范式内部的 终结符,非终结符,或运算,;(表示产生式结束),::=(表示为定义为)
15 在这里不支持闭包,也就是{},因为闭包可以转换为非终结符的递归。
16 输入文本格式 : 要求输入语法为 无回溯语法,在前瞻一个符号的情况下,总能预测正确的产生式规则。
17 start : <aim_name> //aim_name表示起始符号名称
18 例子 :
19 //这是错误的例子,不符合LR(1)语法
20 start : <Goal>;
21 <Goal> ::= <Expr>;
22 <Expr> ::= <Expr> "+" <Term> | <Expr> "-" <Term>;
23 <Term> ::= <Term> "*" <Factor> | <Term> "/" <Factor>;
24 <Factor> ::= "number";
25 #
26
27
28 start : <Goal>;
29 <Goal> ::= <Expr>;
30 <Expr> ::= <Term><Expr'>;
31 <Expr'> ::= "+" <Term><Expr'>
32 | "-" <Term><Expr'>
33 | "ε";
34 <Term> ::= <Factor><Term'>;
35 <Term'> ::= "*" <Factor><Term'>
36 | "/" <Factor><Term'>
37 | "ε";
38 <Factor> ::= "("<Expr>")"
39 | "num"
40 | "name";
41 #
42
43
44 start : <Goal>;
45 <Goal> ::= <List>;
46 <List> ::= <List><Pair>
47 | <Pair>;
48 <Pair> ::= "(" <Pair> ")"
49 | "("")";
50 #
51 以#作为结尾
52 输入分析 : 因为上下文无关语法是一个四元组,而LR(1)语法又是上下文无关语法的子集。所以采用四元组的形式来表示LR(1)语法,是不会损失信息的。
53 四元组 (T,NT,S,P)
54 T : 终结符集合
55 NT : 非终结符集合
56 S : 语法的起始符号(非终结符)
57 P : 产生式集合
58 T, NT都可以用一个hash_set来表示。
59 P 可以分为两个部分,左侧一定是一个非终结符,右侧是一个支持或运算的产生式。
60 产生式左端可以使用Node节点来表示,产生式右端可以使用多个链表(具体有几个取决于当前产生式有多少个或运算符)来表示。
61 将当下语法分为三级,第一级是Expr,第二级别是Term,第三个级别是Factor
62 <Expr> ::= <Term> { "|" <Term>}; //产生式(表达式)可以表达成多个小句子 或 起来
63 <Term> ::= <Factor> { "+" <Factor>}; // + 表示连接
64 <Factor> ::= <T> | <NT>
65 输出 : Action 和 GoTo 表。
66
67 1.0完成进度 : 完成了将 输入字符串 转换成了 中间数据结构(BnfContainer)表示。
68 2.0完成进度 : closure()闭包函数
69 在这里 [A -> β·Cθ,a]指的是,识别完了A后非终结符号为a(也就是LR(1)中的1,前瞻一个符号)。
70 关于FIRST :
71 FIRST(A) : 对于语法符号A,FIRST(A)表示,从A推导出的符号串的第一个单词所对应的终结符的集合。
72 FIRST定义域 : T ∪ NT ∪ {ε, eof}
73 FIRST值域 : T ∪ {ε, eof}
74 如若A等于T ∪ {ε, eof}
75 那么 FIRST(A) = A
76 在闭包函数中,难点在于 FIRST(θa),这不是一个简单的 FIRST(θ),因为多了一个非终结符号 a。
77 这是为了防止FIRST(θ)为ε的情况,这样FIRST(θa)退化为FIRST(a) = {a}
78 closure(s) :
79 while (s is still changing)
80 for each item [A -> β·Cθ,a] ∈ s //从当前s集中寻求新状态
81 for each production C -> γ ∈ P //有效产生式
82 for each b ∈ FIRST(θa) //如若是可能是前瞻符号(非终结符)
83 s <- s ∪ {[C -> ·γ,b]}
84
85 //这里x是语法符号,可以是终结符,也可以是非终结符
86 goto(s, x)
87 moved <- ∅
88 for each item ∈ s //对于s集中的每个项
89 if the from of i is [A -> β·xθ,a] then
90 moved <- moved ∪ {[A -> βx·θ,a]}
91 return closure (moved)
92
93 构建CC的算法:
94 CC0 <- closure({[S' -> ·S,eof]}) //构建初始状态集
95 CC <- {CC0} //将初始状态集添加到规范族CC中
96 while (new set are still being added to CC)
97 for each unmarked set CCi ∈ CC //unmarked : 未标记的
98 mark CCi as processed //将CCi标记为处理过的
99 for each x following a · in an item in CCi //对于CCi中项a ·后面的每个x
100 temp <- goto(CCi, x)
101 if temp ∉ CC
102 then CC <- CC ∪ {temp}
103 record transition from CCi to temp on x
104 例子 :
105 <Goal> ::= <List>;
106 <List> ::= <List> <Pair> | <Pair>;
107 <Pair> ::= "(" <Pair> ")" | "(" ")";
108 closure({[Goal -> ·List,eof]})
109
110 理解 : 将整个BNF范式语句全都替换成非终结符,结果可能会有很多个。
111 但是这可以组成一个DFA,但是许多项都表示的其实是同一种状态,所以需要
112 closure来将这些状态来并到同一个集合内,而goto则是从某个状态集接各种符号
113 ,转移到一个新的状态集,这里即可以是终结符,也可以是非终结符。但是转移后不一定
114 能包含所有的这一状态下的项,所以仍需要闭包运算来完善状态集。
115
116 如何表示一个项?
117 一个项包含三个元素,第一是产生式,第二是 · ,第三是前瞻符号。
118 这可以用三个数字来表示。可以使用一个NODE来表示,
119 但是这样好像就用不了set,来筛选是否重合了。
120 字符串表示法?
121 使用字符串,并且两个中隔符号隔开三个数据。
122 如若需要,再从字符串转换为数字。
123
124 3.0版本完成进度 :
125 填表算法 :
126 Action表 纵轴是状态,横轴是 前瞻符号(终结符),内容是规约,状态转移,接收以及失败。
127 Goto表 纵轴是状态,横轴是 前瞻符号(非终结符),当进行规约操作后,可以依靠栈中之前的状态,
128 加上前瞻的非终结符,来进行状态转移。
129
130 for each CCi ∈ CC
131 for each item I ∈ CCi
132 if I is [A -> β·cθ,a] and goto (CCi , c) = CCj then
133 Action[i,c] <- "shift j"
134 else if I is [A -> β·,a] then //规约
135 Action[i,a] <- "reduce A->B"
136 else if I is [S'->S·,eof] then //如若是目标项产生式推导完成状态并且前瞻符号为eof,则为接收状态。
137 Action[i,eof] <- "accept"
138 for each n ∈ NT //如若项集CCi跳过一个非终结符n即到达j
139 if goto(CCi, n) = CCj then
140 Goto[i,n] <- j
141
142 如何表示几种状态?
143 可以使用位来保证两种状态不混合, shift j直接填入j,而reduce A -> B则或上整型最高位。
144 这个图的状态 对应 书上状态
145 0 - 0
146 1 - 1
147 6 - 6
148 这里r是产生式下标...,而不是表达式下标...。
149 因为我们采用的结构是产生式 -> 表达式,也就是一个产生式连接多个表达式。
150
151 本图 对应 书上图(状态)
152 0 - 0
153 1 - 1
154 6 - 6
155 4 - 4
156 11 - 11
157 9 - 9
158 2 - 3
159 3 - 2
160 5 - 7
161 7 - 5
162 10 - 8
163 8 - 10
164 Action表如下
165 eof ( )
166 0 err s2 err
167 1 acc s2 err
168 2 err s6 s5
169 3 r3 r3 err
170 4 r2 r2 err
171 5 r5 r5 err
172 6 err s6 s8
173 7 err err s10
174 8 err err r5
175 9 err err s11
176 10 r4 r4 err
177 11 err err r4
178 Goto表如下
179 Goal List Pair
180 0 err s1 s3
181 1 err err s4
182 2 err err s7
183 3 err err err
184 4 err err err
185 5 err err err
186 6 err err s9
187 7 err err err
188 8 err err err
189 9 err err err
190 10 err err err
191 11 err err err
192
193
194 */
195 public class Demo2 {
196 public static void main (String[] args) {
197 //将输入的产生式都放入ch中
198 Scanner scanner = new Scanner(System.in);
199 String s = new String();
200 String c;
201 //输入处理...
202 while (true) {
203 c = scanner.nextLine();
204 int i;
205 for (i = 0; i < c.length(); i++) {
206 if (c.charAt(i) != '#')
207 s += c.charAt(i);
208 else {
209 scanner.close();
210 break;
211 }
212 }
213 if (i != c.length()) {
214 break;
215 }
216 }
217 BnfContainer bc = new BnfContainer();
218 CodeAnalyzer ca = new CodeAnalyzer(s, bc);
219 ca.analyze();
220 bc.toLRTable();
221 bc.printActionAndGotoTable();
222 }
223 }
224
225 /**
226 * 用来装载BNF范式的信息。
227 */
228 class BnfContainer {
229 /**
230 * 内部类,NT的节点。
231 * @author HP
232 */
233 class NTNode {
234 private String name; //符号id
235 private List<List<Integer>> expr;
236 public NTNode(String name) {
237 expr = new ArrayList<List<Integer>>();
238 this.name = name;
239 }
240 /**
241 * 添加一条expr
242 * 返回这个expr的下标
243 * @return
244 */
245 public int addExpr() {
246 expr.add(new ArrayList<Integer>());
247 return expr.size() - 1;
248 }
249 /**
250 * 向下标为idx的expr添加value
251 * @param idx
252 * @param value
253 */
254 public void addExprElement (int idx, int value) {
255 this.expr.get(idx).add(value);
256 }
257 /**
258 * 向最后一个表达式添加value
259 * @param value
260 */
261 public void addExprElement (int value) {
262 this.addExprElement(this.expr.size() - 1, value);
263 }
264
265 public void printNTNode () {
266 System.out.println("NTNumber : " + this.name);
267 for (List<Integer> list : this.expr) {
268 for (Integer val : list) {
269 System.out.print(val + " ");
270 }System.out.println();
271 }
272 }
273 }
274
275
276 //常量定义
277 /**
278 * 这两个常量只出现在终结符
279 * 因为要将终结符和非终结符
280 * 放在同一个链表中
281 * 所以使用这个来辨别终结符和非终结符。
282 */
283 private static final int MASK = 0X80000000; //掩码,用来给终结符做掩饰的编码。
284 private static final int DECODE = 0X7fffffff; //解码,破译掩码得到原本的编码。
285 private static final String separationCharacter = " ";
286 /**
287 * 非终结符Map
288 * key : 非终结符名称
289 * value : 非终结符在production链表中的下标
290 */
291 private HashMap<String,Integer> NTMap;
292 /**
293 * 终结符Map
294 * key : 终结符名称
295 * value : 终结符在T链表中的下标
296 */
297 private HashMap<String,Integer> TMap;
298 // 终结符链表
299 private ArrayList<String> T;
300 // 产生式链表,因为一个非终结符一个产生式具有双射关系。
301 private ArrayList<NTNode> production;
302 //如若未设置,默认为0
303 public int startIndex = 0;
304 private int eof, epsilon;
305 /**
306 * 这个数组包含了所有非终结符的FIRST
307 */
308 private Set<Integer>[] First;
309 /**
310 * 要输出的Action表
311 */
312 private int[][] Action;
313 /**
314 * 要输出的Goto表
315 */
316 private int[][] Goto;
317
318 public BnfContainer() {
319 //内部数据结构初始化
320 NTMap = new HashMap<String,Integer>();
321 TMap = new HashMap<String,Integer>();
322 T = new ArrayList<String>();
323 production = new ArrayList<NTNode>();
324
325
326 //添加两个特殊的非终结符 eof 和 ε
327 this.addT("eof");
328 this.addT("ε");
329 eof = this.getTSerialNumber("eof");
330 epsilon = this.getTSerialNumber("ε");
331 }
332
333 /**
334 * 设置开始非终结符
335 * @param name
336 */
337 public void setStart (String name) {
338 this.addNT(name);
339 this.startIndex = this.NTMap.get(name);
340 }
341
342 /**
343 * 将非终结符的名字传入,即可添加一个非终结符节点。
344 * @param name
345 */
346 public void addNT (String name) {
347 if (name.isEmpty()) {
348 System.out.println("终结符不可为空");
349 System.exit(-1);
350 }
351 if (!NTMap.containsKey(name)) {
352 NTNode node = new NTNode(name);
353 NTMap.put(name, production.size());
354 production.add(node);
355 }
356 }
357
358 /**
359 * 将终结符传入,增加非终结符。
360 * @param name
361 */
362 public void addT(String name) {
363 if (!this.TMap.containsKey(name)) {
364 this.TMap.put(name, T.size());
365 this.T.add(name);
366 }
367 }
368
369 /**
370 * 输入终结符名称
371 * 获取终结符编号
372 * 如若存在当前终结符,返回编号
373 * 否则返回-1,输出错误警告并且退出。
374 * @param name
375 * @return
376 */
377 private int getTSerialNumber (String name) {
378 this.notFindTWarning(name);
379 return this.TMap.get(name) | BnfContainer.MASK;
380 }
381
382 /**
383 * 输入非终结符名称
384 * 获取非终结符编号
385 * 如若存在当前非终结符,返回编号
386 * 否则返回-1,输出错误警告并且退出。
387 * @param name
388 * @return
389 */
390 private int getNTSerialNumber (String name) {
391 this.notFindNTWarning(name);
392 return this.NTMap.get(name);
393 }
394
395 /**
396 * 创建新的表达式并添加到名称为name的非终结符节点上
397 * 返回表达式编号
398 */
399 public int creatNewExper(String name) {
400 this.notFindNTWarning(name);
401 NTNode ntn = this.production.get(this.NTMap.get(name));
402 return ntn.addExpr();
403 }
404 /**
405 * 向左端非终结符名称为name的产生式
406 * 第idx表达式添加元素
407 * @param name
408 * @param idx
409 * @param isNt
410 */
411 public void addExpeElement(String name, int idx,boolean isNt, String addElement) {
412 NTNode ntn = this.production.get(this.NTMap.get(name));
413 if (isNt) {
414 this.notFindNTWarning(name);
415 this.notFindNTWarning(addElement);
416 ntn.addExprElement(idx, this.getNTSerialNumber(addElement));
417 }else {
418 this.addT(addElement);
419 ntn.addExprElement(idx, this.getTSerialNumber(addElement));
420 }
421 }
422
423 /**
424 * 向左端非终结符名称为name的产生式
425 * 最后一个表达式添加元素
426 * @param name
427 * @param list
428 */
429 public void addExpeElement(String name,boolean isNt, String addElement) {
430 NTNode ntn = this.production.get(this.NTMap.get(name));
431 if (isNt) {
432 this.notFindNTWarning(name);
433 this.notFindNTWarning(addElement);
434 ntn.addExprElement(this.getNTSerialNumber(addElement));
435 }else {
436 this.addT(addElement);
437 ntn.addExprElement(this.getTSerialNumber(addElement));
438 }
439 }
440
441 /**
442 * 如若找到了当前非终结符,什么都不会发生。
443 * 否则会提示并且退出程序
444 * @param name
445 */
446 private void notFindNTWarning(String name) {
447 if (!this.NTMap.containsKey(name)) {
448 System.out.println("错误的非终结符" + name + "!");
449 System.exit(-1);
450 }
451 }
452 /**
453 * 如若找到了当前终结符,什么都不会发生。
454 * 否则会提示并且退出程序
455 * @param name
456 */
457 private void notFindTWarning(String name) {
458 if (!this.TMap.containsKey(name)) {
459 System.out.println("错误的终结符" + name + "!");
460 System.exit(-1);
461 }
462 }
463
464 public void printBNF() {
465 System.out.println("开始非终结符为 : " + this.production.get(startIndex).name);
466 // System.out.println("终结符对应表 : ");
467 // for (int i = 0; i < this.T.size(); i++) {
468 // System.out.println(this.T.get(i) + " : " + (i | MASK));
469 // }
470 // System.out.println("非终结符对应表 : ");
471 // for (int i = 0; i < this.production.size(); i++) {
472 // System.out.println(this.production.get(i).name + " : " + i);
473 // }
474 for (NTNode ntn : this.production) {
475 ntn.printNTNode();
476 }
477
478 System.out.println("First集 : ");
479 int count = 0;
480 for (Set<Integer> s : First) {
481 System.out.println("第" + count + "个非终结符" + this.production.get(count).name);
482 for (Integer i : s) {
483 this.printSymbol(i);
484 }System.out.println();
485 count++;
486 }
487 System.out.println("一共有 " + this.CC.size() + " 种状态");
488 for (Set<String> s : this.CC) {
489 this.printCCSet(s);
490 }
491 }
492 /**
493 * 输出项集 s
494 * @param s
495 */
496 private void printCCSet(Set<String> s) {
497 for (String item : s) {
498 this.printItem(item);
499 }
500 System.out.println();
501 }
502
503
504 private void printItem (String item) {
505 String[] strs = item.split(BnfContainer.separationCharacter); // ! 为分隔符
506 int productionIdx = Integer.parseInt(strs[0]); //产生式下标
507 int exprIdx = Integer.parseInt(strs[1]); //表达式下标
508 int placeholder = Integer.parseInt(strs[2]); //占位符下标 这个下标从0开始(表示左侧无语法符号)。
509 int prospectiveSymbol = Integer.parseInt(strs[3]);//前瞻符
510 NTNode ntn = this.production.get(productionIdx);
511 System.out.print("[" + ntn.name + "::=");
512 List<Integer> list = ntn.expr.get(exprIdx);
513 for (int i = 0; i < list.size(); i++) {
514 if (i == placeholder) {
515 System.out.print("·");
516 }
517 this.printSymbol(list.get(i));
518 System.out.print(" ");
519 }
520 if (list.size() == placeholder) {
521 System.out.print("·");
522 }
523 System.out.print(",");
524 this.printSymbol(prospectiveSymbol);
525 System.out.print("]\t");
526 }
527
528 private void printSymbol (int sym) {
529 if (this.isT(sym)) {
530 System.out.print(this.T.get(sym & DECODE));
531 }else {
532 System.out.print(this.production.get(sym).name);
533 }
534 }
535
536 /**
537 * 求所有非终结符符号的FIRST集(终结符的FIRST就是它本身)
538 * FIRST(A) : 对于语法符号A,FIRST(A)表示,
539 * 从A推导出的符号串的第一个单词所对应的终结符的集合。
540 */
541 private void FIRSTAllSymbol() {
542 First = new Set[this.production.size()];
543 for (int i = First.length - 1; i >= 0; i--) {
544 FIRST(i);
545 }return;
546 }
547 /**
548 * 输入非终结符下标
549 */
550 private void FIRST(int idx) {
551 if (First[idx] != null) {
552 return;
553 }First[idx] = new HashSet<Integer>();
554 List<List<Integer>> next = this.production.get(idx).expr;
555 for (List<Integer> list : next) {
556 int val = list.get(0);
557 //非终结符
558 if (this.isT(val)) {
559 First[idx].add(val);
560 }else {
561 this.FIRST(val);
562 First[idx].addAll(First[val]);
563 }
564 }
565 }
566
567 private boolean isT (int val) {
568 return (val & MASK) == MASK;
569 }
570 /**
571 * 一个产生式项
572 * 分别有四个元素
573 * productionIdx : 产生式下标
574 * exprIdx : 表达式下标
575 * placeholder : 占位符
576 * prospectiveSymbol : 前瞻符
577 */
578 /**
579 闭包运算
580 closure(s) :
581 while (s is still changing)
582 for each item [A -> β·Cθ,a] ∈ s //从当前s集中寻求新状态
583 for each production C -> γ ∈ P //有效产生式
584 for each b ∈ FIRST(θa) //如若是可能是前瞻符号(非终结符)
585 s <- s ∪ {[C -> ·γ,b]}
586 */
587 private List<Set<String>> CC;
588 private void closure (Set<String> s) {
589 int lastSize = -1;
590 while (lastSize != s.size()) {
591 lastSize = s.size();
592 Set<String> hashset = new HashSet<String>();
593 for (String item : s) {
594 String[] strs = item.split(BnfContainer.separationCharacter); // 为分隔符
595 int productionIdx = Integer.parseInt(strs[0]); //产生式下标
596 int exprIdx = Integer.parseInt(strs[1]); //表达式下标
597 int placeholder = Integer.parseInt(strs[2]); //占位符下标 这个下标从0开始(表示左侧无语法符号)。
598 int prospectiveSymbol = Integer.parseInt(strs[3]);//前瞻符
599 List<Integer> temp = this.production.get(productionIdx).expr.get(exprIdx);
600 //for each item [A -> β·Cθ,a] ∈ s //从当前s集中寻求新状态
601 // for each production C -> γ ∈ P //有效产生式
602 //temp.get(placeholder) 为 这里的 C
603 //条件为 C不是终结符 且 当前占位符未达到最右端 如若C是个终结符,那么就无法拓展,如若占位符已经到达最右端,也无法拓展。
604 if (placeholder < temp.size() && !this.isT(temp.get(placeholder))) {
605 int cIdx = temp.get(placeholder);
606 //先求FIRST(占位符后的串)
607 Set<Integer> set = this.FIRSTNextStr(temp, placeholder + 1, prospectiveSymbol);
608 List<List<Integer>> expr = this.production.get(cIdx).expr;
609 for (int i = 0; i < expr.size(); i++){
610 for (Integer val : set) {
611 String res = cIdx + BnfContainer.separationCharacter + i + BnfContainer.separationCharacter + 0 + BnfContainer.separationCharacter + val;
612 hashset.add(res);
613 }
614 }
615 }
616 }s.addAll(hashset);
617 }
618 /**
619 * 项集之间会有交集,
620 * start : <Goal>;
621 * <Goal> ::= <List>;
622 * <List> ::= <List><Pair>
623 * | <Pair>;
624 * <Pair> ::= "(" <Pair> ")"
625 * | "("")";
626 * #
627 * 书上这个例子的原项 CC0 和 CC1就重复了 [Pair ::= ·(Pair),(]
628 * 当然还有其他的也重复了...
629 */
630 return;
631 }
632 /*
633 goto(s, x)
634 moved <- ∅
635 for each item ∈ s //对于s集中的每个项
636 if the from of i is [A -> β·xθ,a] then
637 moved <- moved ∪ {[A -> βx·θ,a]}
638 return closure (moved)
639 */
640 private Set<String> go (Set<String> s, int x){
641 Set<String> res = new HashSet<String>();
642 for (String item : s) {
643 String[] strs = item.split(BnfContainer.separationCharacter); // ! 为分隔符
644 int productionIdx = Integer.parseInt(strs[0]); //产生式下标
645 int exprIdx = Integer.parseInt(strs[1]); //表达式下标
646 int placeholder = Integer.parseInt(strs[2]); //占位符下标 这个下标从0开始(表示左侧无语法符号)。
647 int prospectiveSymbol = Integer.parseInt(strs[3]);//前瞻符
648 List<Integer> temp = this.production.get(productionIdx).expr.get(exprIdx);
649 String str = new String();
650 if (placeholder + 1 <= temp.size() && temp.get(placeholder) == x) {
651 str = productionIdx + BnfContainer.separationCharacter + exprIdx + BnfContainer.separationCharacter + (placeholder + 1) + BnfContainer.separationCharacter + prospectiveSymbol;
652 res.add(str);
653 }
654 }
655 this.closure(res);
656 return res;
657 }
658
659 /**
660 * 获取 从expr表达式中下标为idx的语法符号开始的串 的FIRST
661 * @param expr
662 * @param idx
663 * @param prospectiveSymbol
664 * @return
665 */
666 private Set<Integer> FIRSTNextStr (List<Integer> expr, int idx, int prospectiveSymbol){
667 Set<Integer> res = new HashSet<Integer>();
668 if (idx >= expr.size()) {
669 res.add(prospectiveSymbol);
670 return res;
671 }
672 //当前符号是终结符
673 if (this.isT(expr.get(idx))) {
674 res.add(expr.get(idx));
675 return res;
676 }
677 res.addAll(First[expr.get(idx)]);
678 //如若存在 epsilon
679 if (res.contains(this.epsilon)) {
680 res.remove(this.epsilon);
681 res.addAll(this.FIRSTNextStr(expr, idx + 1, prospectiveSymbol));
682 }return res;
683 }
684
685 /*
686 CC0 <- closure({[S' -> ·S,eof]}) //构建初始状态集
687 CC <- {CC0} //将初始状态集添加到规范族CC中
688 while (new set are still being added to CC)
689 for each unmarked set CCi ∈ CC //unmarked : 未标记的
690 mark CCi as processed //将CCi标记为处理过的
691 for each x following a · in an item in CCi //对于CCi中项a ·后面的每个x
692 temp <- goto(CCi, x)
693 if temp ∉ CC
694 then CC <- CC ∪ {temp}
695 record transition from CCi to temp on x
696 */
697 /*
698 因为最后生成Action表中需要规约 reduce A - > BC
699 所以需要找到这个表达式的序号为了方便弄一个前缀数组
700 记录在前i个产生式中有多少个表达式。
701 */
702 int[] preArr;
703
704 private void initPreArr() {
705 this.preArr = new int[this.production.size()];
706 if (this.preArr.length > 0) {
707 this.preArr[0] = this.production.get(0).expr.size();
708 for (int i = 1; i < this.preArr.length; i++) {
709 this.preArr[i] = this.preArr[i - 1] + this.production.get(i).expr.size();
710 }
711 }
712 }
713 public void toLRTable() {
714 //初始化。
715 this.initPreArr();
716 this.FIRSTAllSymbol();
717 Set<String> CC0 = new HashSet<String>();
718 List<List<Integer>> expr = this.production.get(startIndex).expr;
719 for (int i = 0; i < expr.size(); i++) {
720 CC0.add(this.startIndex + BnfContainer.separationCharacter + i + BnfContainer.separationCharacter + 0 + BnfContainer.separationCharacter + this.eof);
721 }
722 this.closure(CC0);
723 CC = new ArrayList<Set<String>>();
724 CC.add(CC0);
725 int begin = 0;
726 int lastSize = -1;
727 List<Node> res = new ArrayList<Node>();
728 int endState = -1;
729 while (lastSize != CC.size()) {
730 lastSize = CC.size();
731 for (int i = begin; i < lastSize; i++) {
732 Set<String> s = this.CC.get(i);
733 for (String item : s) {
734 String[] strs = item.split(BnfContainer.separationCharacter); // ! 为分隔符
735 int productionIdx = Integer.parseInt(strs[0]); //产生式下标
736 int exprIdx = Integer.parseInt(strs[1]); //表达式下标
737 int placeholder = Integer.parseInt(strs[2]); //占位符下标 这个下标从0开始(表示左侧无语法符号)。
738 int prospectiveSymbol = Integer.parseInt(strs[3]);//前瞻符
739 List<Integer> list = this.production.get(productionIdx).expr.get(exprIdx);
740 if (placeholder < list.size()) {
741 //因为对于每个项集的每个项的前瞻符都会进行一次推导,所以这里包含所有的推导。我们只需要记录下来就可以生成表了。
742 int x = list.get(placeholder);
743 Set<String> temp = this.go(s, x);
744 int CCj = this.CCcontainsTheSet(temp);
745 if (CCj == -1) {
746 CC.add(temp);
747 CCj = this.CC.size() - 1;
748 }
749 res.add(new Node(i, x, CCj));
750 }
751 //可归约状态
752 else {
753 res.add(new Node(i, prospectiveSymbol, ((productionIdx - 1 >= 0 ? this.preArr[productionIdx - 1] : 0) + exprIdx + 1) | MASK));
754 if (productionIdx == this.startIndex) {
755 endState = i;
756 }
757 }
758 }
759 //更新begins
760 begin = lastSize;
761 }
762 }
763 this.createActionAndGotoTable(res, endState);
764 }
765
766 /**
767 * 这是构建表时临时记录数据的结构
768 */
769 class Node{
770 int state;
771 /**
772 * 对于sym来说就是终结符和非终结符的编码
773 * 也是利用这个来区别到底把val放Action
774 * 表还是Goto表。
775 */
776 int sym;
777 /**
778 * 对于val来说
779 * 如若是产生式规约,则将产生式的下标 | MASK作为val
780 * 如若是正常的状态转移,则直接输入转移状态的下标。
781 */
782 int val;
783
784 public Node(int state, int sym, int val){
785 this.state = state;
786 this.sym = sym;
787 this.val = val;
788 }
789 }
790 /**
791 * 利用这个方法去看规范族CC中是否存在set
792 * 并且会返回set在CC的下标如若存在的话
793 * @param set
794 * @return
795 */
796 private int CCcontainsTheSet (Set<String> set) {
797 for (int i = 0; i < CC.size(); i++) {
798 Set<String> s = CC.get(i);
799 if (s.size() == set.size() && set.containsAll(s)) {
800 return i;
801 }
802 }return -1;
803 }
804 /*
805 for each CCi ∈ CC
806 for each item I ∈ CCi
807 if I is [A -> β·cθ,a] and goto (CCi , c) = CCj then
808 Action[i,c] <- "shift j"
809 else if I is [A -> β·,a] then //规约
810 Action[i,a] <- "reduce A->B"
811 else if I is [S'->S·,eof] then //如若是目标项产生式推导完成状态并且前瞻符号为eof,则为接收状态。
812 Action[i,eof] <- "accept"
813 for each n ∈ NT //如若项集CCi跳过一个非终结符n即到达j
814 if goto(CCi, n) = CCj then
815 Goto[i,n] <- j
816 */
817 private void createActionAndGotoTable(List<Node> node, int endState) {
818 //竖是状态 横是终结符
819 this.Action = new int[this.CC.size()][this.T.size()];
820 //赋初始值
821 for (int i = this.CC.size() - 1; i >= 0; i--) {
822 for (int j = this.T.size() - 1; j >=0; j--) {
823 this.Action[i][j] = -1;
824 }
825 }
826 //竖是状态 横是非终结符
827 this.Goto = new int[this.CC.size()][this.production.size()];//赋初始值
828 for (int i = this.CC.size() - 1; i >= 0; i--) {
829 for (int j = this.production.size() - 1; j >=0; j--) {
830 this.Goto[i][j] = -1;
831 }
832 }
833 for (Node n : node) {
834 //如若跨越的符号是终结符
835 if (this.isT(n.sym)) {
836 Action[n.state][n.sym & DECODE] = n.val;
837 }else {
838 Goto[n.state][n.sym] = n.val;
839 }
840 }
841 //将接受状态设为最低值。
842 this.Action[endState][this.eof & DECODE] = Integer.MIN_VALUE;
843 return;
844 }
845
846
847 public void printActionAndGotoTable() {
848 if (this.Action == null || this.Goto == null) {
849 System.out.println("表未生成,请使用toLRTable函数生成表。");
850 return;
851 }
852 //先输出一行终结符
853 System.out.println("Action表如下");
854 System.out.print("\t");
855 for (int i = 0; i < this.T.size(); i++) {
856 if (i != (this.epsilon & DECODE)) {
857 System.out.print(this.T.get(i) + "\t");
858 }
859 }
860 System.out.print("\n");
861 for (int i = 0; i < this.Action.length; i++) {
862 // 每行第一个输出i
863 System.out.print(i + "\t");
864 for (int j = 0; j < this.Action[i].length; j++) {
865 if (j != (this.epsilon & DECODE)) {
866 if (this.Action[i][j] == -1) {
867 System.out.print("err\t");
868 } // 规约操作
869 else if (this.Action[i][j] == Integer.MIN_VALUE) {
870 System.out.print("acc\t");
871 } else if ((this.Action[i][j] & MASK) == MASK) {
872 System.out.print("r" + (this.Action[i][j] & DECODE) + "\t");
873 } else {
874 System.out.print("s" + this.Action[i][j] + "\t");
875 }
876 }
877 }
878 System.out.print("\n");
879 }
880 System.out.println("Goto表如下");
881 // 先输出一行非终结符
882 System.out.print("\t");
883 for (int i = 0; i < this.production.size(); i++) {
884 System.out.print(this.production.get(i).name + "\t");
885 }
886 System.out.print("\n");
887 for (int i = 0; i < this.Goto.length; i++) {
888 // 每行第一个输出i
889 System.out.print(i + "\t");
890 for (int j = 0; j < this.Goto[i].length; j++) {
891 if (this.Goto[i][j] == -1) {
892 System.out.print("err\t");
893 continue;
894 }
895 System.out.print("s" + this.Goto[i][j] + "\t");
896 }System.out.print("\n");
897 }
898 }
899 }
900
901 /**
902 * 代码分析器 可以将代码转换为信息等价的数据结构
903 */
904 class CodeAnalyzer {
905 class Token{
906 boolean isNt;
907 String name;
908 public Token (boolean isNt, String name) {
909 this.isNt = isNt;
910 this.name = name;
911 }
912 }
913 private char[] text;
914 private int textSize = 0; //字符串有效长度
915 private int point = 0; //text解析进度的指针
916 private BnfContainer bc;
917 private Token token;
918 String left; //左侧非终结符
919 private int count = 0; //记录当前已经解析到哪个产生式了
920 public CodeAnalyzer (String text, BnfContainer bc) {
921 this.bc = bc;
922 //初始化代码分析器
923 this.initText(text);
924 this.initStartSymbol();
925 this.initCodeAnalyzer();
926 }
927 /**
928 * 输入字符串文本,返回处理完毕的字符数组。
929 * @param s
930 * @return
931 */
932 private void initText(String s) {
933 this.text = s.toCharArray();
934 int idx = 0;
935 //将字符串变为一个紧凑的字符数组(去除一些妨碍的字符)
936 while (idx < text.length) {
937 if (text[idx] == '\r' || text[idx] == '\n' || text[idx] == '\t' || text[idx] == ' ') {
938 idx++;
939 }else {
940 text[textSize++] = text[idx++];
941 }
942 }
943 }
944
945 private void initStartSymbol() {
946 // 验证是否存在start:<
947 point = 0;
948 char[] needle = { 's', 't', 'a', 'r', 't', ':', '<' };
949 if (textSize <= needle.length) {
950 this.notFindStartNT();
951 }
952 point = 0;
953 while (point < needle.length) {
954 if (needle[point] == text[point]) {
955 point++;
956 } else {
957 this.notFindStartNT();
958 }
959 }
960 point = needle.length;
961 while (point < textSize && text[point] != '>') {
962 point++;
963 }
964 this.bc.setStart(new String(text, needle.length, point - needle.length));
965 this.skip(Type.RT);
966 this.skip(Type.SEMICOLON);
967 }
968 /**
969 * 通过skip来跳过字符
970 */
971 enum Type{
972 LT, //左尖括号
973 RT, //右尖括号
974 SEMICOLON, //分号
975 QUOTE, //双引号
976 OR, //或
977 COLON, // :
978 EQ, //等于号
979 }
980 private void skip (Type t) {
981 switch(t) {
982 case LT:
983 this.skip('<');
984 break;
985 case RT:
986 this.skip('>');
987 break;
988 case OR:
989 this.skip('|');
990 break;
991 case SEMICOLON:
992 this.skip(';');
993 break;
994 case QUOTE:
995 this.skip('"');
996 break;
997 case COLON:
998 this.skip(':');
999 break;
1000 case EQ:
1001 this.skip('=');
1002 break;
1003 }
1004 }
1005 private void skip (char c) {
1006 if (point >= this.textSize || this.text[point] != c) {
1007 System.out.println("第" + this.count + "个产生式,缺少符号 " + c);
1008 System.exit(-1);
1009 }
1010 point++;
1011 }
1012 /**
1013 * 报错 : 没有找到目标(开始)非终结符号! 并退出程序。
1014 */
1015 private void notFindStartNT() {
1016 System.out.println("没有找到目标非终结符号!");
1017 System.exit(-1);
1018 }
1019
1020 /**
1021 * 之所以一开始就要添加非终结符,而不在解析BNF时候添加
1022 * 是因为,非终结符存在定义的问题,如若 没有定义
1023 * 但有使用(只在右侧出现,未在左侧定义),这个就是错误的。
1024 */
1025 private void initCodeAnalyzer() {
1026 int idx = this.point;
1027 this.point = 0;
1028 this.count = 0;
1029 while (true) {
1030 while (this.point < textSize && text[this.point] != ';') {
1031 this.point++;
1032 }this.point++;
1033 this.count++;
1034 //如若分号后面没有左括号
1035 if (this.point >= textSize) {
1036 break;
1037 }
1038 String name = this.getNT();
1039 bc.addNT(name);
1040 }this.count = 0;
1041 this.point = idx;
1042 }
1043
1044 /**
1045 * BNF
1046 * 从point开始解析字符串。
1047 * <Goal> ::= {<Production>}
1048 * <Production> ::= <左侧非终结符> "::=" <Expr>;
1049 * <Expr> ::= <Term> { "|" <Term>}";";
1050 * <Term> ::= {<Factor>}; //Term在这就是多个终结符或非终结符相连接
1051 * <Factor> ::= <T> | <NT>
1052 */
1053 public void analyze() {
1054 while (point < this.textSize) {
1055 this.count++;
1056 production();
1057 }
1058 }
1059
1060 public void production(){
1061 //先跳过左侧非终结符
1062 this.left = this.getNT();
1063 this.skipDefineSymol();
1064 this.expr();
1065 }
1066 /**
1067 * 跳过 ::=
1068 */
1069 public void skipDefineSymol() {
1070 skip(Type.COLON);
1071 skip(Type.COLON);
1072 skip(Type.EQ);
1073 }
1074 /**
1075 * 获取非终结符
1076 * <xxx>
1077 */
1078 public String getNT () {
1079 skip(Type.LT);
1080 StringBuilder res = new StringBuilder();
1081 while (this.point < this.textSize && text[this.point] != '>') {
1082 res.append(text[this.point++]);
1083 }
1084 skip(Type.RT);
1085 return res.toString();
1086 }
1087
1088 /**
1089 * 当前指针指向 "T" 中第一个"
1090 * @return
1091 */
1092 public String getT() {
1093 this.skip(Type.QUOTE);
1094 StringBuilder res = new StringBuilder();
1095 while (this.point < this.textSize && this.text[this.point] != '"') {
1096 res.append(text[this.point++]);
1097 }
1098 this.skip(Type.QUOTE);
1099 return res.toString();
1100 }
1101
1102 /**
1103 * 当前指针指向 ::= <T>... 中 = 后一个符号
1104 */
1105 public void expr(){
1106 this.term();
1107 while (this.point < this.textSize && text[this.point] == '|') {
1108 this.skip(Type.OR);
1109 term();
1110 }this.skip(Type.SEMICOLON);
1111 }
1112
1113 /**
1114 * 如若还有符号,当前符号指向 终结符或非终结符的符号 < 或者 "
1115 */
1116 public void term(){
1117 //创建一个属于当前term的链表
1118 bc.creatNewExper(this.left);
1119 while (this.point < this.textSize && (text[this.point] == '"' || text[this.point] == '<')) {
1120 factor();
1121 bc.addExpeElement(this.left, token.isNt, token.name);
1122 }
1123 }
1124
1125 /**
1126 * 通过factor获取token
1127 */
1128 public void factor(){
1129 //非终结符
1130 if (text[this.point] == '"') {
1131 String name = this.getT();
1132 this.token = new Token(false, name);
1133 }else {
1134 String name = this.getNT();
1135 token = new Token (true, name);
1136 }
1137 }
1138 }