updata : 附我之前bilibili讲解视频链接 : https://www.bilibili.com/video/av63666423?share_medium=android&share_source=qq&bbid=PQ0-BzIEPAU2VGNXK1crinfoc&ts=1565782566880
目前完成进度 : 目前已经完成了表驱动,通过函数输出这个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 输入 : 输入某文件内存地址。且内部采用::= 14 这里的仅支持 BNF范式内部的 终结符,非终结符,或运算,;(表示产生式结束),::=(表示为定义为) 15 在这里不支持闭包,也就是{},因为闭包可以转换为非终结符的递归。 16 输入文本格式 : 要求输入语法为 无回溯语法,在前瞻一个符号的情况下,总能预测正确的产生式规则。 17 start :+ ; 的结构输入的LR(1)语法。 //aim_name表示起始符号名称 18 例子 : 19 //这是错误的例子,不符合LR(1)语法 20 start :; 21::= 22; ::= 23"+" | "-" ; ::= 24"*" | "/" ; ::= "number"; 25 # 26 27 28 start :; 29::= 30; ::= 31; ::= "+" 32 | "-"33 | "ε"; 34 ::= 35; ::= "*" 36 | "/"37 | "ε"; 38 ::= "(" 39 | "num" 40 | "name"; 41 # 42 43 44 start :")" ; 45::= 46;
::=
47 |
; 48::= "(" 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")" ::= 63{ "|" }; //产生式(表达式)可以表达成多个小句子 或 起来 ::= 64{ "+" }; // + 表示连接 ::= 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| ::= 106;
::=
107
| ; ::= "(" 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")" | "(" ")"; > expr; 236 public NTNode(String name) { 237 expr = new ArrayList
>(); 238 this.name = name; 239 } 240 /** 241 * 添加一条expr 242 * 返回这个expr的下标 243 * @return 244 */ 245 public int addExpr() { 246 expr.add(new ArrayList
()); 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 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 NTMap; 292 /** 293 * 终结符Map 294 * key : 终结符名称 295 * value : 终结符在T链表中的下标 296 */ 297 private HashMap TMap; 298 // 终结符链表 299 private ArrayList T; 300 // 产生式链表,因为一个非终结符一个产生式具有双射关系。 301 private ArrayList production; 302 //如若未设置,默认为0 303 public int startIndex = 0; 304 private int eof, epsilon; 305 /** 306 * 这个数组包含了所有非终结符的FIRST 307 */ 308 private Set [] 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 (); 321 TMap = new HashMap (); 322 T = new ArrayList (); 323 production = new ArrayList (); 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 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 s : this.CC) { 489 this.printCCSet(s); 490 } 491 } 492 /** 493 * 输出项集 s 494 * @param s 495 */ 496 private void printCCSet(Set 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 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 (); 554 List > next = this.production.get(idx).expr; 555 for (List
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 > CC; 588 private void closure (Set s) { 589 int lastSize = -1; 590 while (lastSize != s.size()) { 591 lastSize = s.size(); 592 Set hashset = new HashSet (); 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 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 set = this.FIRSTNextStr(temp, placeholder + 1, prospectiveSymbol); 608 List > 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 :
; 621 *::= 622 *;
::=
623 * |
; 624 *::= "(" 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")" go (Set s, int x){ 641 Set res = new HashSet (); 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 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 FIRSTNextStr (List expr, int idx, int prospectiveSymbol){ 667 Set res = new HashSet (); 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 CC0 = new HashSet (); 718 List > 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
>(); 724 CC.add(CC0); 725 int begin = 0; 726 int lastSize = -1; 727 List res = new ArrayList (); 728 int endState = -1; 729 while (lastSize != CC.size()) { 730 lastSize = CC.size(); 731 for (int i = begin; i < lastSize; i++) { 732 Set 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 list = this.production.get(productionIdx).expr.get(exprIdx); 740 if (placeholder < list.size()) { 741 //因为对于每个项集的每个项的前瞻符都会进行一次推导,所以这里包含所有的推导。我们只需要记录下来就可以生成表了。 742 int x = list.get(placeholder); 743 Set 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 set) { 797 for (int i = 0; i < CC.size(); i++) { 798 Set 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, 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 * ::= { 1048 *} ::= <左侧非终结符> "::=" 1049 *; ::= 1050 *{ "|" }";"; ::= { 1051 *}; //Term在这就是多个终结符或非终结符相连接 ::= 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 *| 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 * 当前指针指向 ::= ... 中 = 后一个符号 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 }